초기소스

This commit is contained in:
jazz
2025-10-29 17:08:05 +09:00
parent e531aa9540
commit 439a379a02
7551 changed files with 1234427 additions and 3 deletions

View File

@@ -0,0 +1,499 @@
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>GC ONE 통합관리자</title>
<!-- CSS -->
<link rel="stylesheet" href="/admin/css/style.css">
<!-- jquery -->
<script src="/admin/vendor/jquery-3.7.1.min.js"></script>
<!-- JS -->
<script src="/admin/js/script.js" defer></script>
<!-- Nice Select -->
<link rel="stylesheet" href="/admin/vendor/jquery-nice-select/nice-select.css">
<script src="/admin/vendor/jquery-nice-select/jquery.nice-select.min.js"></script>
<!-- Date Picker -->
<link rel="stylesheet" href="/admin/vendor/jquery-ui/jquery-ui.min.css">
<link rel="stylesheet" href="/admin/vendor/jquery-ui/jquery-ui.structure.css">
<script src="/admin/vendor/jquery-ui/jquery-ui.min.js"></script>
<!-- 제이쿼리 모달 -->
<script src="/admin/vendor/jquery-modal/jquery.modal.min.js"></script>
<link rel="stylesheet" href="/admin/vendor/jquery-modal/jquery.modal.min.css">
<!--파비콘-->
<link rel="icon" href="/admin/images/gcone.ico">
</head>
<body>
<div class="login__page">
<div class="login__box">
<div class="login">
<h2 class="login__pageTitle">
<div class="login__logoWrapper">
<a href="">
<img class="login__logo active" src="/admin/images/logo_default.png" alt="gcone">
</a>
</div>
<span class="login__pageTitle__text">통합관리자</span>
</h2>
<form id="loginForm">
<input type="hidden" id="realm" th:value="${realm}">
<input type="hidden" id="redirectUri" th:value="${redirectUri}">
<input type="hidden" id="accessUrl" th:value="${accessUrl}">
<input type="hidden" id="sopMalCd" th:value="${sopMalCd}">
<div class="login__inputBox">
<input autofocus class="input-text full" name="username" id="username" type="text" title="아이디"
placeholder="아이디"
oninput="fn_hide_alertTxt('username','mbrIdAlertText');">
<p class="login__inputBox__alertText" id="mbrIdAlertText">아이디를 입력해 주세요.</p>
<input class="input-text full" name="password" id="password" type="password" title="패스워드"
placeholder="비밀번호" onkeyup="fn_hide_alertTxt('password','mbrPwAlertText',event);">
<p class="login__inputBox__alertText" id="mbrPwAlertText">비밀번호를 입력해 주세요.</p>
</div>
<div class="login__misc">
<div class="b-checkbox-container">
<label class="b-checkbox checkbox">
<input type="checkbox" value="">
<span class="check"></span>
<span class="control-label">아이디 저장</span>
</label>
</div>
</div>
<button class="btn full dark login__loginBtn">로그인</button>
</form>
</div>
</div>
</div>
</body>
<!-- 핸드폰 인증(입력) 모달 -->
<div id="phone-auth-02" class="modal" style="max-width: 480px;">
<div class="modal-header">
<h4>핸드폰 인증</h4>
</div>
<div class="modal-content" style="height: 220px;">
<input type="hidden" id="sndngInnb">
<div class="phoneAuth">
<p class="phoneAuth__text">인증번호가 전송되었습니다.</p>
<div class="phoneAuth__inputBox input-container gap-4">
<input type="text" class="input-text text-left" id="mbtlCert" title="인증번호" placeholder="인증번호 입력" maxlength="6">
<button class="btn sm primary" onclick="fn_confim_own()">확인</button>
</div>
<div class="phoneAuthTimer">
<div class="phoneAuthTimer__left">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none">
<path
d="M12 0C5.38125 0 0 5.38125 0 12C0 18.6187 5.38125 24 12 24C18.6187 24 24 18.6187 24 12C24 5.38125 18.6187 0 12 0ZM17.4422 13.6547H11.7656C11.7563 13.6547 11.7422 13.6547 11.7328 13.6547C11.7234 13.6547 11.7094 13.6547 11.7 13.6547C11.1422 13.6547 10.6875 13.2 10.6875 12.6422V4.39219C10.6875 3.83437 11.1422 3.37969 11.7 3.37969C12.2578 3.37969 12.7125 3.83437 12.7125 4.39219V11.625H17.4469C18.0047 11.625 18.4594 12.0797 18.4594 12.6375C18.4594 13.1953 18 13.6547 17.4422 13.6547Z"
fill="#D4D4D4" />
</svg>
<span class="phoneAuthTimer__title">남은시간</span>
<span class="phoneAuthTimer__time" id="timer">03:00</span>
</div>
<button class="phoneAuthTimer__resend" onclick="$('.close-modal').click(); login_second(); event.preventDefault();">재전송</button>
</div>
</div>
</div>
</div>
<script>
/* ********************************************************
* 체크박스 값변경
******************************************************** */
const fn_set_checkbox = function(_target) {
if ($(_target).is(':checked')) {
$(_target).val('Y');
} else {
$(_target).val('N');
}
}
/* ********************************************************
* 아이디 저장 기능 구현
******************************************************** */
document.addEventListener('DOMContentLoaded', function () {
const usernameInput = document.getElementById('username');
const rememberCheckbox = document.querySelector('input[type="checkbox"]');
const savedUsername = localStorage.getItem('savedUsername');
// 페이지 로드 시, 저장된 아이디가 있으면 입력 필드에 채워넣기
if (savedUsername) {
usernameInput.value = savedUsername;
rememberCheckbox.checked = true; // 체크박스도 체크 상태로 유지
}
// 체크박스 상태 변경 시 아이디 저장 또는 삭제
rememberCheckbox.addEventListener('change', function () {
if (this.checked) {
// 체크된 경우, 아이디를 localStorage에 저장
const username = usernameInput.value;
if (username) {
localStorage.setItem('savedUsername', username);
}
} else {
// 체크 해제된 경우, 저장된 아이디 삭제
localStorage.removeItem('savedUsername');
}
});
// 로그인 버튼 클릭 시 아이디 저장 처리
document.querySelector('.login__loginBtn').addEventListener('click', function () {
if (rememberCheckbox.checked) {
const username = usernameInput.value;
if (username) {
localStorage.setItem('savedUsername', username); // 아이디 저장
}
} else {
localStorage.removeItem('savedUsername'); // 체크 해제 시 아이디 삭제
}
});
//아이디 저장시 alert메시지 삭제
fn_hide_alertTxt('username', 'mbrIdAlertText');
});
/* ********************************************************
* 로그인(2차인증)
******************************************************** */
document.getElementById('loginForm').addEventListener('submit', function(e) {
e.preventDefault();
login_second();
});
function login_second(e) {
const username = document.getElementById('username').value.replace(/\s+/g, '');
const password = document.getElementById('password').value.replace(/\s+/g, '');
const accessUrl = document.getElementById('accessUrl').value;
//1. GC_MANAGER 테이블 제한IP, 핸드폰 인증 여부 확인
fetch(`/login/loginCheck`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
username: username,
password: password,
accessUrl: accessUrl
})
})
.then(response => {
console.log(response)
if (response.ok) {
return response.json();
}else{
switch(response.status){
case 401:
throw new Error('회원아이디나 비밀번호를 다시 확인해주세요.');
case 404:
break;
default:
throw new Error('회원 로그인에 실패하였습니다. 관리자에게 문의해주세요.');
}
}
})
.then(data => {
let lmttIpAt = data.lmttIpAt;
let mblphonCrtfcAt = data.mblphonCrtfcAt;
//2. 제한IP 체크 후 로그인 처리
if(lmttIpAt == 'Y'){
//3. 핸드폰 인증여부 체크
if(mblphonCrtfcAt == 'Y'){
// 핸드폰 인증 모달 호출
$('.phoneAuth__text').text(fn_format_mbtlnb(data.mbtlnb).replace(/(\d{3})-(\d{4})-(\d{4})/, "$1-****-$3")+'로 인증번호가 전송되었습니다.');
$('#phone-auth-02').modal('show');
fn_sending_number(data.mngrNm, data.mbtlnb);
}else {
login();
}
}else {
alert('접속 가능한 IP가 아닙니다.')
}
})
.catch(error => {
console.error('Error:', error);
alert('로그인 실패: ' + error.message);
});
}
/* ********************************************************
* 로그인
******************************************************** */
function login() {
const realm = document.getElementById('realm').value;
const username = document.getElementById('username').value.replace(/\s+/g, '');
const password = document.getElementById('password').value.replace(/\s+/g, '');
var redirectUri = document.getElementById('redirectUri').value;
var accessUrl = document.getElementById('accessUrl').value;
const sopMalCd = document.getElementById('sopMalCd').value;
fetch(`/login/${realm}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
username: username,
password: password,
redirectUri: redirectUri,
accessUrl: accessUrl,
sopMalCd: sopMalCd
})
})
.then(response => {
console.log(response)
if (response.ok) {
return response;
}else{
switch(response.status){
case 401:
throw new Error('회원아이디나 비밀번호를 다시 확인해주세요.');
case 404:
break;
default:
throw new Error('회원 로그인에 실패하였습니다. 관리자에게 문의해주세요.');
}
}
})
.then(data => {
console.log('Login successful', data);
const encodedRedirectURL = encodeURIComponent(redirectUri);
window.location.href = '/sso/'+realm+'?redirectUrl='+encodedRedirectURL;
})
.catch(error => {
console.error('Error:', error);
alert('로그인 실패: ' + error.message);
});
}
//로그인 input 이벤트
function fn_hide_alertTxt(targetId, targetAlertId,event) {
const inputLen = document.querySelector("#" + targetId).value.length;
const target = document.querySelector("#"+targetId);
const targetAlert = document.querySelector("#"+targetAlertId);
let message;
if(targetId === 'username'){
message = '아이디를 입력해 주세요.';
}else{
if(inputLen > 0){
if (event.getModifierState("CapsLock")) {
message = 'Caps Lock이 켜져 있습니다.';
}else{
message = '';
}
}else{
message = '비밀번호를 입력해 주세요.';
}
}
const targetHtml = `<p class="login__inputBox__alertText" id = "${targetAlertId}">${message}</p>`;
if (targetAlert) {
targetAlert.remove();
}
if(inputLen > 0){
if (targetId === 'password') {
target.insertAdjacentHTML('afterend', targetHtml);
}
}else{
target.insertAdjacentHTML('afterend', targetHtml);
}
}
/* ********************************************************
* 휴대폰 포매팅
******************************************************** */
const fn_format_mbtlnb = (mbtlnb) => {
if(mbtlnb != '' || mbtlnb !== undefined){
if (mbtlnb.length <= 11) {
if (mbtlnb.length == 11) {
//휴대전호번호 혹은 02를 제외한 전화번호
mbtlnb = mbtlnb.replace(/(\d{3})(\d{4})(\d{4})/, '$1-$2-$3');
} else if (mbtlnb.length == 9) {
//02를 포함한 9자리 전화번호(02-000-0000)
mbtlnb = mbtlnb.replace(/(\d{2})(\d{3})(\d{4})/, '$1-$2-$3');
} else if (mbtlnb.length == 8) {
//대표전화번호 일경우(0000-0000)
mbtlnb = mbtlnb.replace(/(\d{4})(\d{4})/, '$1-$2');
} else {
if (mbtlnb.indexOf('02') == 0) {
//02를 포함한 10자리 전화번호(02-0000-0000)
mbtlnb = mbtlnb.replace(/(\d{2})(\d{4})(\d{4})/, '$1-$2-$3');
} else {
//02를 제외한 10자리 전화번호(032-000-0000)
mbtlnb = mbtlnb.replace(/(\d{3})(\d{3})(\d{4})/, '$1-$2-$3');
}
}
}
}else{
mbtlnb = '';
}
return mbtlnb;
}
/* ********************************************************
* 휴대폰 인증 번호 발송
******************************************************** */
first = 0;
mbtlCert = '';
const fn_sending_number = function(mngrNm, mbtlnb) {
const accessUrl = document.getElementById('accessUrl').value;
if(timerRunning){
return;
}
if(mbtlCert === mbtlnb){
first++;
}else{
first = 0;
}
mbtlCert = mbtlnb;
mbtlnb = mbtlnb.trim().replace(/-/g, '');
// /* 인증번호 임시 */
let authSopmalCd = 'IS';
fetch(`/login/insertPhoneAuth`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
sopMalCd: authSopmalCd,
username: mngrNm,
mbtlnb: mbtlnb,
accessUrl: accessUrl
})
})
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.json(); // JSON으로 응답을 파싱
})
.then(data => {
if(data.result == "success"){
if (first > 0) {
alert('인증번호가 재발송되었습니다.');
} else if (first === 0) {
alert('인증번호가 발송되었습니다.');
first++;
} else {
return;
}
$('#sndngInnb').val(data.sndngInnb);
}else {
alert('인증번호 발송에 실패했습니다.');
$(".close-modal").click();
return;
}
});
clearInterval(countdown);
seconds = 180;
fn_set_timeOut();
countdown = setInterval(fn_set_timeOut, 1000);
timerRunning = true;
}
/* ********************************************************
* 휴대폰 인증 카운터
******************************************************** */
let seconds;
let countdown;
let timerRunning = false;
const fn_set_timeOut = function() {
if (seconds >= 0) {
const minutes = Math.floor(seconds / 60).toString().padStart(2, '0');
const remainingSeconds = (seconds % 60).toString().padStart(2, '0');
$('#timer').text(minutes+":"+remainingSeconds);
seconds--;
} else {
$(".close-modal").click();
alert('인증번호 유효시간이 만료되었습니다.');
}
}
/* ********************************************************
* 타이머 정지 함수
******************************************************** */
const fn_stop_timer = function() {
if (timerRunning) {
clearInterval(countdown);
$('#timer').text('03:00');
$('#mbtlCert').val('');
$('#phone-auth-02').hide();
timerRunning = false;
}
}
/* ********************************************************
* 휴대폰 인증 확인
******************************************************** */
isChkd_own = false;
const fn_confim_own = function() {
if (first <= 0) return;
let sndngInnb = $('#sndngInnb').val();
let mbtlnb = mbtlCert.trim().replace(/-/g, '');
let crtnb = $('#mbtlCert').val();
let accessUrl = document.getElementById('accessUrl').value;
fn_stop_timer();
// /* 인증번호 임시 */
fetch(`/login/selectPhoneAuth`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
sndngInnb: sndngInnb,
mbtlnb: mbtlnb,
crtnb: crtnb,
accessUrl: accessUrl
})
})
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.json(); // JSON으로 응답을 파싱
})
.then(data => {
if(data.result == "success"){
alert('인증되었습니다.');
isChkd_own = true;
login();
}else {
alert('인증번호가 일치하지 않습니다.');
$(".close-modal").click();
return;
}
})
}
$(document).on("click", ".close-modal", function() {
fn_stop_timer();
});
</script>
</html>

View File

@@ -0,0 +1,218 @@
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>GC ONE 인트라넷</title>
<!-- CSS -->
<link rel="stylesheet" href="/itrnt/css/style.css">
<!-- jquery -->
<script src="/itrnt/vendor/jquery-3.7.1.min.js"></script>
<!-- JS -->
<script src="/itrnt/js/script.js" defer></script>
<!-- Nice Select -->
<link rel="stylesheet" href="/itrnt/vendor/jquery-nice-select/nice-select.css">
<script src="/itrnt/vendor/jquery-nice-select/jquery.nice-select.min.js"></script>
<!-- Date Picker -->
<link rel="stylesheet" href="/itrnt/vendor/jquery-ui/jquery-ui.min.css">
<link rel="stylesheet" href="/itrnt/vendor/jquery-ui/jquery-ui.structure.css">
<script src="/itrnt/vendor/jquery-ui/jquery-ui.min.js"></script>
<!-- 제이쿼리 모달 -->
<script src="/itrnt/vendor/jquery-modal/jquery.modal.min.js"></script>
<link rel="stylesheet" href="/itrnt/vendor/jquery-modal/jquery.modal.min.css">
</head>
<body>
<div class="login__page">
<div class="login__box">
<div class="login">
<h2 class="login__pageTitle">
<div class="login__logoWrapper">
<a href="">
<img class="login__logo active" src="/itrnt/images/logo_default.png" alt="gcone">
</a>
</div>
<span class="login__pageTitle__text">인트라넷</span>
</h2>
<form id="loginForm">
<div class="login__inputBox">
<input type="hidden" id="realm" th:value="${realm}">
<input type="hidden" id="redirectUri" th:value="${redirectUri}">
<input type="hidden" id="accessUrl" th:value="${accessUrl}">
<input type="hidden" id="sopMalCd" th:value="${sopMalCd}">
<input type="hidden" id="authSopmalCd" value="">
<input autofocus class="input-text full" name="username" id="username" type="text" title="아이디" placeholder="아이디"
oninput="fn_hide_alertTxt('username','mbrIdAlertText');">
<p class="login__inputBox__alertText" id="mbrIdAlertText">아이디를 입력해 주세요.</p>
<input class="input-text full" name="password" id="password" type="password" title="패스워드"
placeholder="비밀번호" onkeyup="fn_hide_alertTxt('password','mbrPwAlertText',event);">
<p class="login__inputBox__alertText" id="mbrPwAlertText">비밀번호를 입력해 주세요.</p>
</div>
<div class="login__misc">
<div class="b-checkbox-container">
<label class="b-checkbox checkbox">
<input type="checkbox" value="">
<span class="check"></span>
<span class="control-label">아이디 저장</span>
</label>
</div>
</div>
<button class="btn full dark login__loginBtn">로그인</button>
</form>
</div>
</div>
</div>
<script>
/* ********************************************************
* 체크박스 값변경
******************************************************** */
const fn_set_checkbox = function(_target) {
if ($(_target).is(':checked')) {
$(_target).val('Y');
} else {
$(_target).val('N');
}
}
/* ********************************************************
* 아이디 저장 기능 구현
******************************************************** */
document.addEventListener('DOMContentLoaded', function () {
const usernameInput = document.getElementById('username');
const rememberCheckbox = document.querySelector('input[type="checkbox"]');
const savedUsername = localStorage.getItem('savedUsername');
// 페이지 로드 시, 저장된 아이디가 있으면 입력 필드에 채워넣기
if (savedUsername) {
usernameInput.value = savedUsername;
rememberCheckbox.checked = true; // 체크박스도 체크 상태로 유지
}
// 체크박스 상태 변경 시 아이디 저장 또는 삭제
rememberCheckbox.addEventListener('change', function () {
if (this.checked) {
// 체크된 경우, 아이디를 localStorage에 저장
const username = usernameInput.value;
if (username) {
localStorage.setItem('savedUsername', username);
}
} else {
// 체크 해제된 경우, 저장된 아이디 삭제
localStorage.removeItem('savedUsername');
}
});
// 로그인 버튼 클릭 시 아이디 저장 처리
document.querySelector('.login__loginBtn').addEventListener('click', function () {
if (rememberCheckbox.checked) {
const username = usernameInput.value;
if (username) {
localStorage.setItem('savedUsername', username); // 아이디 저장
}
} else {
localStorage.removeItem('savedUsername'); // 체크 해제 시 아이디 삭제
}
});
//아이디 저장시 alert메시지 삭제
fn_hide_alertTxt('username', 'mbrIdAlertText');
});
/* ********************************************************
* 로그인
******************************************************** */
document.getElementById('loginForm').addEventListener('submit', function(e) {
e.preventDefault();
const realm = document.getElementById('realm').value;
const username = document.getElementById('username').value;
const password = document.getElementById('password').value;
const redirectUri = document.getElementById('redirectUri').value;
const accessUrl = document.getElementById('accessUrl').value;
const sopMalCd = document.getElementById('sopMalCd').value;
fetch(`/login/${realm}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
username: username,
password: password,
redirectUri: redirectUri,
accessUrl: accessUrl,
sopMalCd: sopMalCd
})
})
.then(response => {
console.log(response)
if (response.ok) {
return response;
}else{
switch(response.status){
case 401:
throw new Error('회원아이디나 비밀번호를 다시 확인해주세요.');
case 404:
break;
default:
throw new Error('회원 로그인에 실패하였습니다. 관리자에게 문의해주세요.');
}
}
})
.then(data => {
console.log('Login successful', data);
const encodedRedirectURL = encodeURIComponent(redirectUri);
window.location.href = '/sso/'+realm+'?redirectUrl='+encodedRedirectURL;
})
.catch(error => {
console.error('Error:', error);
alert('로그인 실패: ' + error.message);
});
});
//로그인 input 이벤트
function fn_hide_alertTxt(targetId, targetAlertId,event) {
const inputLen = document.querySelector("#" + targetId).value.length;
const target = document.querySelector("#"+targetId);
const targetAlert = document.querySelector("#"+targetAlertId);
let message;
if(targetId === 'username'){
message = '아이디를 입력해 주세요.';
}else{
if(inputLen > 0){
if (event.getModifierState("CapsLock")) {
message = 'Caps Lock이 켜져 있습니다.';
}else{
message = '';
}
}else{
message = '비밀번호를 입력해 주세요.';
}
}
const targetHtml = `<p class="login__inputBox__alertText" id = "${targetAlertId}">${message}</p>`;
if (targetAlert) {
targetAlert.remove();
}
if(inputLen > 0){
if (targetId === 'password') {
target.insertAdjacentHTML('afterend', targetHtml);
}
}else{
target.insertAdjacentHTML('afterend', targetHtml);
}
}
</script>
</body>
</html>

View File

@@ -0,0 +1,535 @@
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>GC ONE 관리자</title>
<!-- CSS -->
<link rel="stylesheet" href="/mngr/css/style.css">
<!-- jquery -->
<script src="/mngr/vendor/jquery-3.7.1.min.js"></script>
<!-- JS -->
<script src="/mngr/js/script.js" defer></script>
<!-- Nice Select -->
<link rel="stylesheet" href="/mngr/vendor/jquery-nice-select/nice-select.css">
<script src="/mngr/vendor/jquery-nice-select/jquery.nice-select.min.js"></script>
<!-- Date Picker -->
<link rel="stylesheet" href="/mngr/vendor/jquery-ui/jquery-ui.min.css">
<link rel="stylesheet" href="/mngr/vendor/jquery-ui/jquery-ui.structure.css">
<script src="/mngr/vendor/jquery-ui/jquery-ui.min.js"></script>
<!-- 제이쿼리 모달 -->
<script src="/mngr/vendor/jquery-modal/jquery.modal.min.js"></script>
<link rel="stylesheet" href="/mngr/vendor/jquery-modal/jquery.modal.min.css">
<!--파비콘-->
<link rel="icon" href="/mngr/images/gcone.ico">
</head>
<body>
<div class="login__page">
<div class="login__box">
<div class="login">
<h2 class="login__pageTitle">
<div class="login__logoWrapper">
<a href="">
<img class="login__logo active" src="/mngr/images/logo_default.png" alt="gcone">
<img class="login__logo" th:each="site : ${siteList}" th:data-brand="${site.sopmalCd}"
th:src="@{${cdnurl}+${site.topFilCours}+'/'+${site.topSysFilNm}}" th:alt="${site.sopmalNm}">
</a>
<div class="header__siteSelectArrow"><span class="visually-hidden">사이트 선택</span></div>
<div class="siteSelect">
<ul class="siteSelect__list" style="width: 300%">
<li th:each="site : ${siteList}">
<button class="siteSelect__btn" th:id="${site.sopmalCd}" th:text="${site.sopmalNm}"></button>
</li>
</ul>
</div>
</div>
<span class="login__pageTitle__text">관리자</span>
</h2>
<form id="loginForm">
<div class="login__inputBox">
<input type="hidden" id="realm" th:value="${realm}">
<input type="hidden" id="redirectUri" th:value="${redirectUri}">
<input type="hidden" id="accessUrl" th:value="${accessUrl}">
<input type="hidden" id="sopMalCd" th:value="${sopMalCd}">
<input type="hidden" id="authSopmalCd" value="">
<input autofocus class="input-text full" name="username" id="username" type="text" title="아이디" placeholder="아이디"
oninput="fn_hide_alertTxt('username','mbrIdAlertText');">
<p class="login__inputBox__alertText" id="mbrIdAlertText">아이디를 입력해 주세요.</p>
<input class="input-text full" name="password" id="password" type="password" title="패스워드"
placeholder="비밀번호" onkeyup="fn_hide_alertTxt('password','mbrPwAlertText',event);">
<p class="login__inputBox__alertText" id="mbrPwAlertText">비밀번호를 입력해 주세요.</p>
</div>
<div class="login__misc">
<div class="b-checkbox-container">
<label class="b-checkbox checkbox">
<input type="checkbox" value="">
<span class="check"></span>
<span class="control-label">아이디 저장</span>
</label>
</div>
</div>
<button class="btn full dark login__loginBtn">로그인</button>
</form>
</div>
</div>
</div>
</body>
<!-- 핸드폰 인증(입력) 모달 -->
<div id="phone-auth-02" class="modal" style="max-width: 480px;">
<div class="modal-header">
<h4>핸드폰 인증</h4>
</div>
<div class="modal-content" style="height: 220px;">
<input type="hidden" id="sndngInnb">
<div class="phoneAuth">
<p class="phoneAuth__text">인증번호가 전송되었습니다.</p>
<div class="phoneAuth__inputBox input-container gap-4">
<input type="text" class="input-text text-left" id="mbtlCert" title="인증번호" placeholder="인증번호 입력" maxlength="6">
<button class="btn sm primary" onclick="fn_confim_own()">확인</button>
</div>
<div class="phoneAuthTimer">
<div class="phoneAuthTimer__left">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none">
<path
d="M12 0C5.38125 0 0 5.38125 0 12C0 18.6187 5.38125 24 12 24C18.6187 24 24 18.6187 24 12C24 5.38125 18.6187 0 12 0ZM17.4422 13.6547H11.7656C11.7563 13.6547 11.7422 13.6547 11.7328 13.6547C11.7234 13.6547 11.7094 13.6547 11.7 13.6547C11.1422 13.6547 10.6875 13.2 10.6875 12.6422V4.39219C10.6875 3.83437 11.1422 3.37969 11.7 3.37969C12.2578 3.37969 12.7125 3.83437 12.7125 4.39219V11.625H17.4469C18.0047 11.625 18.4594 12.0797 18.4594 12.6375C18.4594 13.1953 18 13.6547 17.4422 13.6547Z"
fill="#D4D4D4" />
</svg>
<span class="phoneAuthTimer__title">남은시간</span>
<span class="phoneAuthTimer__time" id="timer">03:00</span>
</div>
<button class="phoneAuthTimer__resend" onclick="$('.close-modal').click(); login_second(); event.preventDefault();">재전송</button>
</div>
</div>
</div>
</div>
<script>
$(document).ready(function () {
$('.login__logo').click(function () {
$('.login__logo').removeClass('active');
$(this).addClass('active');
});
$('.header__siteSelectArrow').click(function () {
$('.siteSelect').toggleClass('active');
});
$('.siteSelect__btn').click(function () {
$('.siteSelect').removeClass('active');
$('.login__logo').removeClass('active');
let brand = $(this).attr('id');
$('.login__logo[data-brand="' + brand + '"]').addClass('active');
$('#authSopmalCd').val(brand); // 기본값을 제공
});
//아이디 저장시 alert메시지 삭제
fn_hide_alertTxt('username','mbrIdAlertText');
});
/* ********************************************************
* 체크박스 값변경
******************************************************** */
const fn_set_checkbox = function(_target) {
if ($(_target).is(':checked')) {
$(_target).val('Y');
} else {
$(_target).val('N');
}
}
/* ********************************************************
* 아이디 저장 기능 구현
******************************************************** */
document.addEventListener('DOMContentLoaded', function () {
const usernameInput = document.getElementById('username');
const rememberCheckbox = document.querySelector('input[type="checkbox"]');
const savedUsername = localStorage.getItem('savedUsername');
// 페이지 로드 시, 저장된 아이디가 있으면 입력 필드에 채워넣기
if (savedUsername) {
usernameInput.value = savedUsername;
rememberCheckbox.checked = true; // 체크박스도 체크 상태로 유지
}
// 체크박스 상태 변경 시 아이디 저장 또는 삭제
rememberCheckbox.addEventListener('change', function () {
if (this.checked) {
// 체크된 경우, 아이디를 localStorage에 저장
const username = usernameInput.value;
if (username) {
localStorage.setItem('savedUsername', username);
}
} else {
// 체크 해제된 경우, 저장된 아이디 삭제
localStorage.removeItem('savedUsername');
}
});
});
/* ********************************************************
* 로그인(2차인증)
******************************************************** */
document.getElementById('loginForm').addEventListener('submit', function(e) {
e.preventDefault();
login_second();
});
function login_second(e) {
const username = document.getElementById('username').value.replace(/\s+/g, '');
const password = document.getElementById('password').value.replace(/\s+/g, '');
const accessUrl = document.getElementById('accessUrl').value;
//1. GC_MANAGER 테이블 제한IP, 핸드폰 인증 여부 확인
fetch(`/login/loginCheck`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
username: username,
password: password,
accessUrl: accessUrl
})
})
.then(response => {
console.log(response)
if (response.ok) {
return response.json();
}else{
switch(response.status){
case 401:
throw new Error('회원아이디나 비밀번호를 다시 확인해주세요.');
case 404:
break;
default:
throw new Error('회원 로그인에 실패하였습니다. 관리자에게 문의해주세요.');
}
}
})
.then(data => {
let lmttIpAt = data.lmttIpAt;
let mblphonCrtfcAt = data.mblphonCrtfcAt;
//2. 제한IP 체크 후 로그인 처리
if(lmttIpAt == 'Y'){
//3. 핸드폰 인증여부 체크
if(mblphonCrtfcAt == 'Y'){
// 핸드폰 인증 모달 호출
$('.phoneAuth__text').text(fn_format_mbtlnb(data.mbtlnb).replace(/(\d{3})-(\d{4})-(\d{4})/, "$1-****-$3")+'로 인증번호가 전송되었습니다.');
$('#phone-auth-02').modal('show');
fn_sending_number(data.mngrNm, data.mbtlnb);
}else {
login();
}
}else {
alert('접속 가능한 IP가 아닙니다.')
}
})
.catch(error => {
console.error('Error:', error);
alert('로그인 실패: ' + error.message);
});
}
/* ********************************************************
* 로그인
******************************************************** */
function login() {
const realm = document.getElementById('realm').value;
const username = document.getElementById('username').value.replace(/\s+/g, '');
const password = document.getElementById('password').value.replace(/\s+/g, '');
var redirectUri = document.getElementById('redirectUri').value;
var accessUrl = document.getElementById('accessUrl').value;
const sopMalCd = document.getElementById('sopMalCd').value;
const authSopmalCd = document.getElementById('authSopmalCd').value;
fetch(`/login/${realm}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
username: username,
password: password,
redirectUri: redirectUri,
accessUrl: accessUrl,
sopMalCd: sopMalCd
})
})
.then(response => {
console.log(response)
if (response.ok) {
return response;
}else{
switch(response.status){
case 401:
throw new Error('회원아이디나 비밀번호를 다시 확인해주세요.');
case 404:
break;
default:
throw new Error('회원 로그인에 실패하였습니다. 관리자에게 문의해주세요.');
}
}
})
.then(data => {
console.log('Login successful', data);
let moveTo = redirectUri;
// 로그아웃 상태에서 redirectUri 가 crm 인경우 안먹는 문제 해결
const firstPath = moveTo.split('/')[3]; // "/"를 기준으로 분리
if (firstPath === 'crm') {
console.log('CRM 페이지로 리다이렉션 처리');
if(redirectUri === '') moveTo = '/mngr/main?authSopmalCd=\'+authSopmalCd'
const encodedRedirectURL = encodeURIComponent(moveTo);
window.location.href = '/sso/'+realm+'?redirectUrl='+encodedRedirectURL;
}else{
if(authSopmalCd !== '') {
if(moveTo.endsWith("/mngr")){
moveTo = redirectUri +'/main?authSopmalCd='+authSopmalCd;
}else{
moveTo = redirectUri +'/mngr/main?authSopmalCd='+authSopmalCd;
}
}
const encodedRedirectURL = encodeURIComponent(moveTo);
window.location.href = '/sso/'+realm+'?redirectUrl='+encodedRedirectURL;
}
})
.catch(error => {
console.error('Error:', error);
alert('로그인 실패: ' + error.message);
});
}
//로그인 input 이벤트
function fn_hide_alertTxt(targetId, targetAlertId,event) {
const inputLen = document.querySelector("#" + targetId).value.length;
const target = document.querySelector("#"+targetId);
const targetAlert = document.querySelector("#"+targetAlertId);
let message;
if(targetId === 'username'){
message = '아이디를 입력해 주세요.';
}else{
if(inputLen > 0){
if (event.getModifierState("CapsLock")) {
message = 'Caps Lock이 켜져 있습니다.';
}else{
message = '';
}
}else{
message = '비밀번호를 입력해 주세요.';
}
}
const targetHtml = `<p class="login__inputBox__alertText" id = "${targetAlertId}">${message}</p>`;
if (targetAlert) {
targetAlert.remove();
}
if(inputLen > 0){
if (targetId === 'password') {
target.insertAdjacentHTML('afterend', targetHtml);
}
}else{
target.insertAdjacentHTML('afterend', targetHtml);
}
}
/* ********************************************************
* 휴대폰 포매팅
******************************************************** */
const fn_format_mbtlnb = (mbtlnb) => {
if(mbtlnb != '' || mbtlnb !== undefined){
if (mbtlnb.length <= 11) {
if (mbtlnb.length == 11) {
//휴대전호번호 혹은 02를 제외한 전화번호
mbtlnb = mbtlnb.replace(/(\d{3})(\d{4})(\d{4})/, '$1-$2-$3');
} else if (mbtlnb.length == 9) {
//02를 포함한 9자리 전화번호(02-000-0000)
mbtlnb = mbtlnb.replace(/(\d{2})(\d{3})(\d{4})/, '$1-$2-$3');
} else if (mbtlnb.length == 8) {
//대표전화번호 일경우(0000-0000)
mbtlnb = mbtlnb.replace(/(\d{4})(\d{4})/, '$1-$2');
} else {
if (mbtlnb.indexOf('02') == 0) {
//02를 포함한 10자리 전화번호(02-0000-0000)
mbtlnb = mbtlnb.replace(/(\d{2})(\d{4})(\d{4})/, '$1-$2-$3');
} else {
//02를 제외한 10자리 전화번호(032-000-0000)
mbtlnb = mbtlnb.replace(/(\d{3})(\d{3})(\d{4})/, '$1-$2-$3');
}
}
}
}else{
mbtlnb = '';
}
return mbtlnb;
}
/* ********************************************************
* 휴대폰 인증 번호 발송
******************************************************** */
first = 0;
mbtlCert = '';
const fn_sending_number = function(mngrNm, mbtlnb) {
const accessUrl = document.getElementById('accessUrl').value;
if(timerRunning){
return;
}
if(mbtlCert === mbtlnb){
first++;
}else{
first = 0;
}
mbtlCert = mbtlnb;
mbtlnb = mbtlnb.trim().replace(/-/g, '');
// /* 인증번호 임시 */
let authSopmalCd = 'IS';
fetch(`/login/insertPhoneAuth`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
sopMalCd: authSopmalCd,
username: mngrNm,
mbtlnb: mbtlnb,
accessUrl: accessUrl
})
})
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.json(); // JSON으로 응답을 파싱
})
.then(data => {
if(data.result == "success"){
if (first > 0) {
alert('인증번호가 재발송되었습니다.');
} else if (first === 0) {
alert('인증번호가 발송되었습니다.');
first++;
} else {
return;
}
$('#sndngInnb').val(data.sndngInnb);
}else {
alert('인증번호 발송에 실패했습니다.');
$(".close-modal").click();
return;
}
});
clearInterval(countdown);
seconds = 180;
fn_set_timeOut();
countdown = setInterval(fn_set_timeOut, 1000);
timerRunning = true;
}
/* ********************************************************
* 휴대폰 인증 카운터
******************************************************** */
let seconds;
let countdown;
let timerRunning = false;
const fn_set_timeOut = function() {
if (seconds >= 0) {
const minutes = Math.floor(seconds / 60).toString().padStart(2, '0');
const remainingSeconds = (seconds % 60).toString().padStart(2, '0');
$('#timer').text(minutes+":"+remainingSeconds);
seconds--;
} else {
$(".close-modal").click();
alert('인증번호 유효시간이 만료되었습니다.');
}
}
/* ********************************************************
* 타이머 정지 함수
******************************************************** */
const fn_stop_timer = function() {
if (timerRunning) {
clearInterval(countdown);
$('#timer').text('03:00');
$('#mbtlCert').val('');
$('#phone-auth-02').hide();
timerRunning = false;
}
}
/* ********************************************************
* 휴대폰 인증 확인
******************************************************** */
isChkd_own = false;
const fn_confim_own = function() {
if (first <= 0) return;
let sndngInnb = $('#sndngInnb').val();
let mbtlnb = mbtlCert.trim().replace(/-/g, '');
let crtnb = $('#mbtlCert').val();
let accessUrl = document.getElementById('accessUrl').value;
fn_stop_timer();
// /* 인증번호 임시 */
fetch(`/login/selectPhoneAuth`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
sndngInnb: sndngInnb,
mbtlnb: mbtlnb,
crtnb: crtnb,
accessUrl: accessUrl
})
})
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.json(); // JSON으로 응답을 파싱
})
.then(data => {
if(data.result == "success"){
alert('인증되었습니다.');
isChkd_own = true;
login();
}else {
alert('인증번호가 일치하지 않습니다.');
$(".close-modal").click();
return;
}
})
}
$(document).on("click", ".close-modal", function() {
fn_stop_timer();
});
</script>
</html>

View File

@@ -0,0 +1,895 @@
<!DOCTYPE html>
<html lang="ko" xmlns:th="http://www.w3.org/1999/html">
<head>
<!-- 메타정보 -->
<meta charset="UTF-8">
<!-- M자형 디스플레이 위해 "viewport-fit=cover" 속성 필요 -->
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1, viewport-fit=cover">
<meta name="description" th:content="${shopInfo.metaDc}">
<meta name="keywords" th:content="${shopInfo.metaKwrd}">
<!-- CSS -->
<!-- 공통 CSS-->
<link rel="stylesheet" href="/sopmall/mobile/css/default.css">
<!-- 테마 CSS-->
<link rel = 'stylesheet' th:href="|/sopmall/mobile${shopInfo.cssCours}|" th:if="${#strings.startsWith(shopInfo.cssCours, '/')}">
<link rel = 'stylesheet' th:href="|/sopmall/mobile/${shopInfo.cssCours}|" th:if="${!#strings.startsWith(shopInfo.cssCours, '/')}">
<!-- 파비콘-->
<link rel="icon" th:href="|/sopmall${shopInfo.fviconCours}|" th:if="${#strings.startsWith(shopInfo.fviconCours, '/')}">
<link rel="icon" th:href="|/sopmall/${shopInfo.fviconCours}|" th:if="${!#strings.startsWith(shopInfo.fviconCours, '/')}">
<!-- 타이틀-->
<title th:text="${shopInfo.metaSj}"></title>
<!-- jquery -->
<script src="/sopmall/mobile/vendor/jquery-3.7.1.min.js"></script>
<!--js-->
<!-- micromodal -->
<!-- https://github.com/Ghosh/micromodal -->
<script src="/sopmall/mobile/vendor/micromodal/micromodal.min.js"></script>
<!-- popModal -->
<link rel="stylesheet" type="text/css" href="/sopmall/mobile/vendor/popModal/popModal.min.css">
<script src="/sopmall/mobile/vendor/popModal/popModal.min.js"></script>
<style>
/* 기본적으로 모든 .boxTab__content 요소는 숨김 처리 */
.boxTab__content {
display: none;
}
/* current 클래스가 있는 경우에만 표시 */
.boxTab__content.current {
display: block;
}
</style>
</head>
<script>
$(document).ready(function(){
////햄버거 메뉴 관련 스크립트
// 좌측 메뉴 클릭시 해당 메뉴 보여주기
$('.fullMenu__menu__btn').on('click', function () {
$(this).addClass('current');
$(this).closest('li').siblings().find('.fullMenu__menu__btn').removeClass('current');
var index = $(this).closest('li').index() - 1; // 전체 메뉴 버튼을 뺀 나머지 버튼의 인덱스
$(this).closest('.fullMenu__content').find('.fullMenu__itemSection').eq(index).siblings().hide();
$(this).closest('.fullMenu__content').find('.fullMenu__itemSection').eq(index).show();
});
// 전체 메뉴 클릭시 전체 메뉴 보여주기
$('.fullMenuAll').on('click', function () {
$(this).addClass('current');
$(this).closest('li').siblings().find('.fullMenu__menu__btn').removeClass('current');
$(this).closest('.fullMenu__content').find('.fullMenu__itemSection').show();
});
//
$('.hamburgerTabBtn').on('click', function () {
$('.hamburgerTabBtn').removeClass('active');
$(this).addClass('active');
var index = $(this).closest('li').index();
$('.fullMenu__content').removeClass('active');
$('.fullMenu__content').eq(index).addClass('active');
});
MicroModal.init({
disableScroll: true,
awaitCloseAnimation: true,
});
});
/* ************************************************************
* 모바일 모달 show 함수
* modalId : 콜백 함수 이름
************************************************************* */
const openModal = (modalId) => {
MicroModal.show(modalId, {
onShow: function (modal) {
// 이미 존재하지 않는 경우에만 Hash 추가
if (window.location.hash !== '#' + modalId) {
window.history.pushState({modal: 'open'}, '', '#' + modalId);
}
}
});
if(modalId === 'hamburger-menu'){
window.history.replaceState({}, '', window.location.href.split('#')[0]);
}
}
/* ************************************************************
* 모바일 모달 닫고, URL Hash 제거
* modalId : 콜백 함수 이름
************************************************************* */
const closeModal = (modalId) => {
// 모달 닫힐 때 body 스크롤 복원
document.body.style.overflow = '';
// MicroModal을 사용해 모달 닫기
MicroModal.close(modalId);
// 추가적인 히스토리 항목을 생성하지 않고 URL에서 #이후부터 Hash 제거
window.history.replaceState({}, '', window.location.href.split('#')[0]);
}
/**************************************************************
* input
* 특정 input(select)에 특정 event 발생시 처리할 함수 지정
* 파라미터 설명{
* selector : 대상 input Object selector
* eventType : 대상 event종류(예시>keyup)
* callback : 대상 event발생 시 호출될 callback함수
* }
****************************************************************/
const fn_set_event = function (selector, eventType, callback) {
document.querySelector(selector).addEventListener(eventType, function () {
let target = this;
callback(target);
});
}
/**************************************************************
* 데이터 NULL 여부
* 파라미터 설명 {
* data : 데이터
* }
****************************************************************/
const fn_is_empty = (data) => {
if (typeof data === 'undefined' ||
data === 'undefined' ||
data === null ||
data === '' ||
data === 'null' ||
data.length === 0 ||
(typeof data === 'object' && !Object.keys(data).length)) {
return true;
} else {
return false;
}
}
/**************************************************************
* 포매팅 (input 값 포매팅)
* 전화번호(일반전화번호, 휴대전화번호, 대표번호) 자동 포매팅(-) 설정
* 파라미터 설명{
* target : 대상 Object
* }
****************************************************************/
const fn_fmt_telno = function (target) {
// 숫자만 허용
target.value = target.value.replace(/[^0-9]/g, '');
if (target.value.length <= 11) {
if (target.value.length == 11) {
// 휴대전화번호 혹은 02를 제외한 전화번호
target.value = target.value.replace(/(\d{3})(\d{4})(\d{4})/, '$1-$2-$3');
} else if (target.value.length == 9) {
// 02를 포함한 9자리 전화번호(02-000-0000)
target.value = target.value.replace(/(\d{2})(\d{3})(\d{4})/, '$1-$2-$3');
} else if (target.value.length == 8) {
// 대표전화번호 일 경우(0000-0000)
target.value = target.value.replace(/(\d{4})(\d{4})/, '$1-$2');
} else {
if (target.value.indexOf('02') == 0) {
// 02를 포함한 10자리 전화번호(02-0000-0000)
target.value = target.value.replace(/(\d{2})(\d{4})(\d{4})/, '$1-$2-$3');
} else {
// 02를 제외한 10자리 전화번호(032-000-0000)
target.value = target.value.replace(/(\d{3})(\d{3})(\d{4})/, '$1-$2-$3');
}
}
}
}
/* ********************************************************
* 주문 상세 이동
******************************************************** */
const fn_go_ordr_dtl = function () {
var accessUrl = document.getElementById('accessUrl').value;
if(accessUrl.startsWith("http://") && !(accessUrl.includes("dev.") || accessUrl.includes("loc."))){
accessUrl = accessUrl.replace("http://", "https://");
}
var form = document.getElementById('condForm');
form.setAttribute('method', 'post');
form.setAttribute('action', accessUrl+'/sopmal/main/selectNMbrOrdrDtlInqire');
form.submit();
}
/* ********************************************************
* 주문 번호 조회
******************************************************** */
const fn_select_oderNumbber = function() {
let sopMalCd = document.getElementById('sopMalCd').value;
let ordrInnb = document.getElementById('ordrInnb').value;
let mbtlnb = document.getElementById('mbtlnb').value;
let accessUrl = document.getElementById('accessUrl').value;
let mbtlnbEle = document.getElementById('mbtlnb');
if (fn_is_empty(ordrInnb)) {
alert("주문번호를 입력해주세요.");
return false;
}
if (fn_is_empty(mbtlnb)) {
alert("주문 시 입력한 휴대폰번호를 입력해주세요.");
return false;
}
// let params = {
// 'sopMalCd': sopMalCd,
// 'ordrInnb': ordrInnb,
// 'mbtlnb': mbtlnb
// };
const form = document.getElementById('condForm');
const formData = new FormData(form); // 폼 데이터
// fetch('/sopmal/main/selectNMbrMbtlnbInqire', {
fetch('/login/selectNMbrMbtlnbInqire', {
method: 'POST',
// body: formData
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
sopMalCd: sopMalCd,
ordrInnb: ordrInnb,
mbtlnb: mbtlnb,
accessUrl: accessUrl
})
})
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.json();
})
.then(data => {
// let result = data.resBody;
// let parsedResult = JSON.parse(result.result);
// let confirmAt = parsedResult.confirmAt;
let confirmAt = data.confirmAt;
if (confirmAt === 'Y') {
fn_go_ordr_dtl();
} else {
alert("휴대폰번호가 일치하지않습니다.");
mbtlnbEle.focus();
}
})
.catch(error => {
alert("비회원 주문조회 실패");
console.error('Error:', error);
});
}
/* ********************************************************
* 체크박스 값변경
******************************************************** */
const fn_set_checkbox = function(_target) {
if ($(_target).is(':checked')) {
$(_target).val('Y');
} else {
$(_target).val('N');
}
}
/* ********************************************************
* 아이디 저장 기능 구현
******************************************************** */
document.addEventListener('DOMContentLoaded', function () {
const usernameInput = document.getElementById('username');
const rememberCheckbox = document.querySelector('input[type="checkbox"]');
const savedUsername = localStorage.getItem('savedUsername');
// 페이지 로드 시, 저장된 아이디가 있으면 입력 필드에 채워넣기
if (savedUsername) {
usernameInput.value = savedUsername;
rememberCheckbox.checked = true; // 체크박스도 체크 상태로 유지
}
// 체크박스 상태 변경 시 아이디 저장 또는 삭제
rememberCheckbox.addEventListener('change', function () {
if (this.checked) {
// 체크된 경우, 아이디를 localStorage에 저장
const username = usernameInput.value;
if (username) {
localStorage.setItem('savedUsername', username);
}
} else {
// 체크 해제된 경우, 저장된 아이디 삭제
localStorage.removeItem('savedUsername');
}
});
// 로그인 버튼 클릭 시 아이디 저장 처리
document.querySelector('.login__loginBtn').addEventListener('click', function () {
if (rememberCheckbox.checked) {
const username = usernameInput.value;
if (username) {
localStorage.setItem('savedUsername', username); // 아이디 저장
}
} else {
localStorage.removeItem('savedUsername'); // 체크 해제 시 아이디 삭제
}
});
});
document.addEventListener("DOMContentLoaded", function() {
// 서브 탭 메뉴 타입 2
var boxTabs = document.querySelectorAll(".boxTab__tabBtn");
var contents = document.querySelectorAll(".boxTab__content");
function resetBoxTabs() {
boxTabs.forEach(function(tab) {
tab.closest("li").classList.remove("active");
});
contents.forEach(function(content) {
content.classList.remove("current");
});
}
boxTabs.forEach(function(tab, index) {
tab.addEventListener("click", function() {
resetBoxTabs();
tab.closest("li").classList.add("active");
contents[index].classList.add("current");
});
});
// 상단 이동 스크롤 버튼
const scrollToTop = document.querySelector(".quick-menu__to-top > button");
scrollToTop.addEventListener('click', function () {
window.scrollTo({top: 0, behavior: 'smooth'});
});
// 2401016 햄버거 메뉴 수정
$('.menu_navigation_wrap .depth').click(function (e) {
e.preventDefault();
if ($(this).parent().hasClass('on')) {
$(this).parent().next().slideUp();
$(this).parent().removeClass('on');
return false;
}
$('.menu_navigation_wrap .depth').parent().removeClass('on');
$('.menu_navigation_wrap .view').slideUp();
$('.menu_navigation_wrap .view .subview').slideUp();
$('.menu_navigation_wrap .view .on').removeClass('on');
$(this).parent().next().slideToggle();
$(this).parent().toggleClass('on');
return false;
});
$('.menu_navigation_wrap .sublist .hasSub').click(function (e) {
if ($(this).parent().hasClass('on')) {
$(this).parent().next().slideUp();
$(this).parent().removeClass('on');
return false;
}
e.preventDefault();
$('.menu_navigation_wrap .sublist .hasSub').parent().removeClass('on');
$('.menu_navigation_wrap .sublist .subview').slideUp();
$(this).parent().next().slideToggle();
$(this).parent().toggleClass('on');
return false;
});
$(".menu_navigation_wrap .depth").on('click', function () {
/*하위레벨 없을 시 페이지 이동*/
if ($(this).closest(".isSub").find(".view").length === 0) {
const href = $(this).attr("href");
location.href = href;
}
});
//하위 카테고리 없을 시 view 클래스 삭제
$(".view").each(function(){
if($(this).find("li").length === 0){
$(this).remove();
}
});
//아이디 저장시 alert메시지 삭제
fn_hide_alertTxt('username', 'mbrIdAlertText');
});
</script>
<body>
<div class="screen-wrap">
<!-- 헤더 영역 -->
<header class="header header--sub">
<div class="header__wrapper">
<div class="header__controls-wrapper">
<div class="header__controls">
<div class="header__toBack">
<button class="header__toBack__btn" onclick="window.history.back();">
<span class="visually-hidden" >뒤로가기</span>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none"
viewBox="0 0 24 24">
<path fill="#1A1A1A"
d="M16.598 3.338a.75.75 0 0 0-1.061 0l-8.132 8.131a.75.75 0 0 0 0 1.061l8.132 8.132a.75.75 0 0 0 1.06-1.06L8.998 12l7.6-7.602a.75.75 0 0 0 0-1.06Z">
</path>
</svg>
</button>
</div>
<h1 class="header__title">로그인</h1>
</div>
</div>
</div>
<nav class="headerTab">
<div class="headerTab__content">
<ul class="headerTab__list">
<li class="headerTab__item active">
<a class="boxTab__tabBtn" href="javascript:void(0);">회원</a>
</li>
<li class="headerTab__item">
<a class="boxTab__tabBtn" href="javascript:void(0);">비회원(주문조회)</a>
</li>
</ul>
</div>
</nav>
</header>
<!-- 하단 탭바 -->
<aside class="tab-bar">
<nav class="tab-bar__nav">
<a class="tab-bar__link" onclick="openModal('hamburger-menu');">
<i class="icon icon_tab-bar-category"></i>
카테고리</a>
<a class="tab-bar__link" th:href="${accessUrl + '/sopmal/mpage/ordr/selectWslistInqireForm'}">
<i class="icon icon_tab-bar-wishlist"></i>
위시리스트</a>
<div class="tab-bar__home-wrapper">
<div class="tab-bar__home">
<a th:href="${accessUrl + '/index'}" class="hide-text">
<i class="icon icon_tab-bar-home"></i>
<span class="visually-hidden">홈</span>
</a>
</div>
</div>
<a class="tab-bar__link" th:href="${accessUrl + '/sopmal/ordr/selectSpbk'}">
<i class="icon icon_tab-bar-cart"></i>
장바구니</a>
<a class="tab-bar__link active" th:href="${accessUrl + '/sopmal/mpage/mbr/selectMpageMainInqire'}">
<i class="icon icon_tab-bar-mypage"></i>
마이페이지
</a>
</nav>
<div class="tab-bar__bg">
<div class="tab-bar__bg-fraction"></div>
</div>
</aside>
<!-- 메인 컨텐츠 -->
<div class="content-wrap">
<main class="main">
<div class="sub-page">
<div class="sub-box">
<div class="login">
<div class="boxTab__content login-content current">
<div class="login__logoContainer">
<a th:href="${accessUrl}">
<img th:src="@{${cdnurl}+${shopInfo.topFilCours}+'/'+${shopInfo.topSysFilNm}}" th:alt="${shopInfo.sopmalNm}">
</a>
</div>
<form id="loginForm">
<input type="hidden" id="realm" th:value="${realm}">
<input type="hidden" id="redirectUri" th:value="${redirectUri}">
<input type="hidden" id="accessUrl" th:value="${accessUrl}">
<input type="hidden" id="sopMalCd" th:value="${sopMalCd}">
<div class="login__inputBox">
<input class="input-text full " type="text" name="username" id="username" title="아이디"
placeholder="아이디" oninput="fn_hide_alertTxt('username','mbrIdAlertText');">
<p class="login__inputBox__alertText"><span id="mbrIdAlertText">아이디를 입력해 주세요.</span></p>
<input class="input-text full " type="password" name="password" id="password" title="비밀번호"
placeholder="비밀번호" onkeyup="fn_hide_alertTxt('password','mbrPwAlertText',event);">
<p class="login__inputBox__alertText"><span id="mbrPwAlertText">비밀번호를 입력해 주세요.</span></p>
</div>
<div class="login__loginBtnContainer">
<button class="btn xl full dark login__loginBtn">로그인</button>
</div>
<div class="login__misc">
<div class="b-checkbox-container">
<label class="b-checkbox is-medium checkbox">
<input type="checkbox" value="N" name="" onchange="fn_set_checkbox(this);">
<span class="check"></span>
<span class="control-label">아이디 저장</span>
</label>
</div>
<a class="login__misc__link" th:href="${accessUrl + '/sopmal/mbr/selectIdPasswordSearch'}">아이디/비밀번호 찾기</a>
</div>
</form>
<div class="login__signIn">
<p class="login__signIn__text">아직 <span th:text="${shopInfo.sopmalNm}"></span> 회원이 아니세요?</p>
<a class="login__signIn__link" th:href="${accessUrl + '/sopmal/mbr/selectJoinGuidance'}">회원가입</a>
</div>
<div class="login__sns">
<th:block th:if="${shopInfo.kakaoIntrlckAt == 'Y'}">
<button type="button" class="login__sns__btn kakao" onclick="openSocialLoginPopup('KAKAO')"><i class="icon icon_kakaoLogin"></i>카카오
로그인</button>
</th:block>
<th:block th:if="${shopInfo.naverIntrlckAt == 'Y'}">
<button type="button" class="login__sns__btn naver" onclick="openSocialLoginPopup('NAVER')"><i class="icon icon_naverLogin"></i>네이버
로그인</button>
</th:block>
<th:block th:if="${purchasingAt == 'Y'}">
<button class="login__sns__btn gray" onclick="nmbrPurchsForm()">비회원으로 구매하기</button>
</th:block>
</div>
</div>
<div class="boxTab__content login-content">
<div class="login__logoContainer">
<a th:href="${accessUrl}">
<img th:src="@{${cdnurl}+${shopInfo.topFilCours}+'/'+${shopInfo.topSysFilNm}}" th:alt="${shopInfo.sopmalNm}">
</a>
</div>
<form id="condForm">
<div class="login__inputBox">
<input class="input-text full " type="text" name="ordrInnb" id="ordrInnb" title="주문번호"
placeholder="주문번호" oninput="fn_hide_alertTxt('ordrInnb','nmbrOrdrAlertText');">
<p class="login__inputBox__alertText"><span id="nmbrOrdrAlertText">주문번호를 입력해 주세요.</span></p>
<input class="input-text number full " type="tel" name="mbtlnb" id="mbtlnb" title="휴대폰번호"
placeholder="주문 시 입력한 휴대폰번호('-'없이)" oninput="fn_hide_alertTxt('mbtlnb','nmbrPwAlertText');">
<p class="login__inputBox__alertText"><span id="nmbrPwAlertText">휴대폰번호를 입력해 주세요.</span></p>
</div>
<div class="login__loginBtnContainer">
<button type="button" class="btn xl full dark login__loginBtn" onclick="fn_select_oderNumbber();">조회</button>
</div>
</form>
</div>
</div>
</div>
</div>
<!-- Top 버튼 -->
<div class="quick-menu__to-top">
<button><i class="icon icon_quick-to-top"></i>
<span class="visually-hidden">위로 가기</span>
</button>
</div>
<div class="searchPage">
<div class="searchPage__inner">
<form id="search-form">
<div class="searchPage__header">
<div class="innerSearch">
<input id="header-search-bar" name="kwrd" type="search" placeholder="검색어를 입력해 주세요" title="검색어 입력">
<button class="searchPage__textDeleteBtn">
<svg width="16" height="16" viewBox="0 0 16 16" fill="none"
xmlns="http://www.w3.org/2000/svg">
<circle cx="8.00023" cy="7.99974" r="6.93333" fill="#b3b3b3"></circle>
<path d="M5.06689 5.06641L10.9336 10.9331M5.06689 10.9331L10.9336 5.06641"
stroke="#ffffff" stroke-width="1.25" stroke-miterlimit="10"></path>
</svg>
</button>
<button id="search_btn"><i class="icon icon_nav-search"></i></button>
</div>
<div class="header-search-pop__auto-complete">
<ul>
</ul>
</div>
<button type="button" class="searchPage__closeBtn" onclick="fn_search_close();">
<span class="visually-hidden">검색어 삭제</span>
<svg xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" width="24" height="24"
viewBox="0 0 24 24">
<path
d="M 4.9902344 3.9902344 A 1.0001 1.0001 0 0 0 4.2929688 5.7070312 L 10.585938 12 L 4.2929688 18.292969 A 1.0001 1.0001 0 1 0 5.7070312 19.707031 L 12 13.414062 L 18.292969 19.707031 A 1.0001 1.0001 0 1 0 19.707031 18.292969 L 13.414062 12 L 19.707031 5.7070312 A 1.0001 1.0001 0 0 0 18.980469 3.9902344 A 1.0001 1.0001 0 0 0 18.292969 4.2929688 L 12 10.585938 L 5.7070312 4.2929688 A 1.0001 1.0001 0 0 0 4.9902344 3.9902344 z">
</path>
</svg>
</button>
</div>
</form>
<div class="searchPage__body">
<div class="searchPage__section">
<div class="searchPage__sectionHeader">
<h5 class="searchPage__title">최근 검색어</h5>
<button class="recentSearch__deleteAll">전체삭제</button>
</div>
<div class="recentSearch">
<ul class="recentSearch__list">
</ul>
</div>
</div>
</div>
</div>
</div>
</main>
</div>
</div>
<!-- 햄버거 메뉴 모달 -->
<div class="microModal microFullMenu" id="hamburger-menu" aria-hidden="true">
<div class="microModal__overlay" tabindex="-1">
<div class="microModal__container" role="dialog" aria-modal="true">
<div class="microModal__body" id = "hambergerModal">
<div class="microModal__top">
<h2 class="microModal__title">
전체메뉴
</h2>
<button class="microModal__close" onclick="closeModal('hamburger-menu');"></button>
</div>
<div class="microModal__content">
<div class="fullMenu">
<div class="sub-box">
<div class="fullMenu__top">
<button class="btn md outline" onclick="fn_mobileButton('/login');">로그인</button>
<button class="btn md theme"onclick="fn_mobileButton('/sopmal/mbr/selectJoinGuidance');">회원가입</button>
</div>
</div>
<div class="sub-box">
<div class="fullMenu__top">
<div class="member-rank-summary">
<div class="textBtns">
<a th:href="${accessUrl + '/index'}" class="textBtns__btn">홈으로</a>
<a th:href="${accessUrl + '/sopmal/ordr/selectSpbk'}" class="textBtns__btn">장바구니</a>
<a th:href="${accessUrl + '/login'}" class="textBtns__btn">주문조회</a>
</div>
</div>
</div>
</div>
<div class="product-tab">
<ul>
<li><button class="product-tab__btn hamburgerTabBtn"></button></li>
</ul>
</div>
<div class="sub-box">
<div class="menu_navigation_wrap">
<ul class="side_menu accordion" th:if="${not category.isEmpty()}">
<th:block th:each="topMenuList : ${category}">
<th:block th:if="${topMenuList.menuExpsrAt eq 'Y'}">
<th:block th:if="${topMenuList.rnum == 2}">
<li class="isSub list">
<div class="isSubDv">
<a th:href="${accessUrl + topMenuList.url}" class="depth" th:text="${topMenuList.ctgryNm}">
<em class="visually-hidden">상세내용닫기</em>
</a>
<a th:href="${accessUrl + topMenuList.url}" class="moer"></a>
</div>
<ul class="view">
<th:block th:each="subMenu : ${category}">
<th:block th:if="${subMenu.rnum == 3}">
<th:block th:if="${topMenuList.ctgryId eq subMenu.upperCtgryId}">
<li><a th:href="${accessUrl + subMenu.url}" th:text="${subMenu.ctgryNm}"></a></li>
</th:block>
</th:block>
</th:block>
</ul>
</li>
</th:block>
</th:block>
</th:block>
</ul>
</div>
</div>
<div class="sub-box">
<div class="fullMenu__miscLinks">
<h5 class="fullMenu__miscLinks__title">서비스</h5>
<ul class="fullMenu__miscLinks__list">
<li>
<a th:href="${accessUrl + '/sopmal/csc/selectCscMainForm'}" class="fullMenu__miscLinks__link">고객센터</a>
</li>
</ul>
</div>
</div>
<div class="sub-box" th:if="${shopInfo.cscTelnbOrdrPosblAt == 'Y'}">
<div class="mainNotice__customerContact mainNotice__customerContact--moreInfo">
<i class="icon icon_notice03"></i>
<div class="mainNotice__customerContact__right">
<h4 class="notice__title customer-service">고객센터</h4>
<span class="notice__number" th:text="${shopInfo.cscTelnb}"></span>
<p class="notice__paragraph">
<span class="notice__text01" th:text="${shopInfo.cscWkdayWorkTme}"></span>
<span class="notice__text02" th:text="@{|(${shopInfo.cscLunchTme})|}"></span>
<br>
<span class="notice__text03" th:text="${shopInfo.cscRstde}"></span>
</p>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
/* ********************************************************
* 로그인
******************************************************** */
document.getElementById('loginForm').addEventListener('submit', function(e) {
e.preventDefault();
const realm = document.getElementById('realm').value;
const username = document.getElementById('username').value.replace(/\s+/g, '');
const password = document.getElementById('password').value.replace(/\s+/g, '');
const redirectUri = document.getElementById('redirectUri').value;
const accessUrl = document.getElementById('accessUrl').value;
const sopMalCd = document.getElementById('sopMalCd').value;
const id = document.querySelector("#username");
const pw = document.querySelector("#password");
let validateTarget;
//validate
if (fn_is_empty(id.value)) {
validateTarget = 'id';
} else if (fn_is_empty(pw.value)) {
validateTarget = 'pw';
}else{
validateTarget = 'none';
}
fetch(`/login/${realm}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
username: username,
password: password,
redirectUri: redirectUri,
accessUrl: accessUrl,
sopMalCd: sopMalCd
})
})
.then(response => {
console.log(response)
if (response.ok) {
return response;
}else{
switch(response.status){
case 401:
if(validateTarget === 'id'){
throw new Error('아이디를 입력해주세요.');
}else if(validateTarget === 'pw'){
throw new Error('비밀번호를 입력해주세요.');
}else{
throw new Error('회원아이디나 비밀번호를 다시 확인해주세요.');
}
case 404:
break;
default:
throw new Error('회원 로그인에 실패하였습니다. 관리자에게 문의해주세요.');
}
}
})
.then(data => {
console.log('Login successful', data);
let moveTo = redirectUri;
if(redirectUri === '') moveTo = '/'
window.location.href = data.url;
})
.catch(error => {
console.error('Error:', error);
alert(error.message);
if (validateTarget === 'id') {
id.focus();
} else if (validateTarget === 'pw') {
pw.focus();
}
});
});
/* ********************************************************
* 로그아웃
******************************************************** */
const logout = () => {
fetch('/logout', {
method: 'GET',
credentials: 'include'
}).then(response => {
if (response.ok) {
window.location.href = '/login';
} else {
console.error('Logout failed');
}
}).catch(error => {
console.error('Error:', error);
});
}
/* ********************************************************
* SNS 소셜 로그인
******************************************************** */
// 웹뷰 환경 감지 함수 추가
function isWebView() {
const ua = navigator.userAgent.toLowerCase();
return ua.includes('wv') ||
ua.includes('webview') ||
window.ReactNativeWebView !== undefined ||
(window.webkit && window.webkit.messageHandlers);
}
const openSocialLoginPopup = (provider) => {
console.log("openSocialLoginPopup");
var redirectUri = document.getElementById('redirectUri').value;
var sopMalCd = document.getElementById('sopMalCd').value;
var accessUrl = document.getElementById('accessUrl').value;
var tmpProvider = sopMalCd + '-' + provider;
var url = '/oauth2/authorization/' + tmpProvider +
'?access_url=' + encodeURIComponent(accessUrl) +
'&redirect_uri=' + encodeURIComponent(redirectUri) +
"&oauth2_type=login";
// 웹뷰 환경인지 확인
if (isWebView()) {
console.log("웹뷰 환경");
// 웹뷰에서는 현재 창에서 직접 리다이렉트
window.location.href = url;
} else {
console.log("일반 브라우저 환경");
// 일반 브라우저에서는 팝업 사용
var width = 600;
var height = 600;
var left = (screen.width / 2) - (width / 2);
var top = (screen.height / 2) - (height / 2);
window.open(url, 'SocialLoginPopup',
'toolbar=no, location=no, directories=no, status=no, menubar=no, scrollbars=no, resizable=no, copyhistory=no, width=' +
width + ', height=' + height + ', top=' + top + ', left=' + left);
}
}
const receiveMessage = (event) => {
if(event.origin !== window.location.origin) {
return;
}
if(event.data.type === 'LOGIN_SUCCESS') {
window.location.href = event.data.redirectUrl;
}
}
window.addEventListener('message', receiveMessage, false);
//비회원 주문 폼 이동
function nmbrPurchsForm(){
var accessUrl = document.getElementById('accessUrl').value;
window.location.href = accessUrl+'/sopmal/ordr/selectNmbrPurchsForm';
}
//로그인,주문조회 input 이벤트
function fn_hide_alertTxt(targetId, targetAlertId,event) {
const inputLen = document.querySelector("#" + targetId).value.length;
const target = document.querySelector("#" + targetAlertId);
if (inputLen > 0) {
if (targetId === 'password') {
if (event.getModifierState("CapsLock")) {
target.textContent = "Caps Lock이 켜져 있습니다.";
target.style.display = 'block';
} else {
target.style.display = 'none';
}
} else {
target.style.display = 'none';
}
} else {
if (targetId === 'password') {
target.textContent = "비밀번호를 입력해주세요.";
}
target.style.display = 'block'
}
}
//로그인/회원가입 버튼 클릭
function fn_mobileButton(url){
var accessUrl = document.getElementById('accessUrl').value;
window.location.href = accessUrl+url;
}
</script>
</body>
</html>

View File

@@ -0,0 +1,101 @@
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<title>OAuth2 Redirect</title>
</head>
<body>
<input type="hidden" id="redirectUri" th:value="${redirectUri}">
<input type="hidden" id="accessUrl" th:value="${accessUrl}">
<input type="hidden" id="joinMthCd" th:value="${joinMthCd}">
<input type="hidden" id="realm" th:value="${realm}">
<input type="hidden" id="sopMalCd" th:value="${sopMalCd}">
<input type="hidden" id="extrlIntrlckId" th:value="${extrlIntrlckId}">
<input type="hidden" id="mbtlnb" th:value="${mbtlnb}">
<input type="hidden" id="mbrNm" th:value="${mbrNm}">
<input type="hidden" id="email" th:value="${email}">
<input type="hidden" id="sex" th:value="${sex}">
<input type="hidden" id="brthDe" th:value="${brthDe}">
<script>
// 웹뷰 환경 감지 함수
function isWebView() {
const ua = navigator.userAgent.toLowerCase();
return ua.includes('wv') ||
ua.includes('webview') ||
window.ReactNativeWebView !== undefined ||
(window.webkit && window.webkit.messageHandlers);
}
// 안전한 창 닫기 함수
function safeCloseWindow() {
try {
// 웹뷰가 아니고 opener가 있는 경우에만 close 시도
if (!isWebView() && window.opener && window.opener !== window) {
window.close();
return true;
}
} catch (e) {
console.warn('Window close failed:', e);
}
return false;
}
var redirectUri = document.getElementById('redirectUri').value;
var accessUrl = document.getElementById('accessUrl').value;
var joinMthCd = document.getElementById('joinMthCd').value;
var realm = document.getElementById('realm').value;
var sopMalCd = document.getElementById('sopMalCd').value;
var extrlIntrlckId = document.getElementById('extrlIntrlckId').value;
var mbtlnb = document.getElementById('mbtlnb').value;
var mbrNm = document.getElementById('mbrNm').value;
var email = document.getElementById('email').value;
var sex = document.getElementById('sex').value;
var brthDe = document.getElementById('brthDe').value;
if(brthDe != '') {
brthDe = (brthDe.length === 4) ? '1900' + brthDe : (brthDe.length === 2) ? '1900' + brthDe + '01' : brthDe;
brthDe = brthDe.slice(0, -2) + '01';
}
if (window.opener) {
if (redirectUri.includes('failSnsLogin')) {
redirectUri = "/login";
alert("전화번호가 없는경우 SNS 회원가입이 불가능 합니다.");
}
// 부모 창에 메시지 전송
try {
window.opener.postMessage({
type: 'LOGIN_SUCCESS',
redirectUrl: redirectUri,
joinMthCd: joinMthCd,
realm: realm,
sopMalCd: sopMalCd,
extrlIntrlckId: extrlIntrlckId,
mbtlnb: mbtlnb,
mbrNm: mbrNm,
email: email,
sex: sex,
brthDe: brthDe
}, accessUrl);
} catch (e) {
console.warn('PostMessage failed:', e);
}
// 안전한 창 닫기 시도
const closed = safeCloseWindow();
// 창 닫기가 실패한 경우 (웹뷰 환경 등)
if (!closed) {
// 3초 후 리다이렉트로 fallback
setTimeout(() => {
window.location.href = redirectUri;
}, 3000);
}
} else {
// 팝업이 아닌 경우, 직접 리다이렉트
window.location.href = redirectUri;
}
</script>
</body>
</html>

View File

@@ -0,0 +1,441 @@
<!DOCTYPE html>
<html lang="ko" xmlns:th="http://www.w3.org/1999/html">
<head>
<!-- 메타정보-->
<meta charset="UTF-8">
<meta name="description" th:content="${shopInfo.metaDc}">
<meta name="keywords" th:content="${shopInfo.metaKwrd}">
<!-- CSS -->
<!-- 공통 CSS-->
<link rel="stylesheet" href="/sopmall/pc/css/default.css">
<!-- 테마 CSS-->
<link rel = 'stylesheet' th:href="|/sopmall/pc${shopInfo.cssCours}|" th:if="${#strings.startsWith(shopInfo.cssCours, '/')}">
<link rel = 'stylesheet' th:href="|/sopmall/pc/${shopInfo.cssCours}|" th:if="${!#strings.startsWith(shopInfo.cssCours, '/')}">
<!-- 파비콘-->
<link rel = 'icon' th:href="|/sopmall${shopInfo.fviconCours}|" th:if="${#strings.startsWith(shopInfo.fviconCours, '/')}">
<link rel = 'icon' th:href="|/sopmall/${shopInfo.fviconCours}|" th:if="${!#strings.startsWith(shopInfo.fviconCours, '/')}">
<!-- 타이틀-->
<title th:text="${shopInfo.metaSj}"></title>
<!--js-->
<script src="/sopmall/pc/js/common.js"></script>
</head>
<script>
/**************************************************************
* input
* 특정 input(select)에 특정 event 발생시 처리할 함수 지정
* 파라미터 설명{
* selector : 대상 input Object selector
* eventType : 대상 event종류(예시>keyup)
* callback : 대상 event발생 시 호출될 callback함수
* }
****************************************************************/
const fn_set_event = function (selector, eventType, callback) {
document.querySelector(selector).addEventListener(eventType, function () {
let target = this;
callback(target);
});
}
/**************************************************************
* 데이터 NULL 여부
* 파라미터 설명 {
* data : 데이터
* }
****************************************************************/
const fn_is_empty = (data) => {
if (typeof data === 'undefined' ||
data === 'undefined' ||
data === null ||
data === '' ||
data === 'null' ||
data.length === 0 ||
(typeof data === 'object' && !Object.keys(data).length)) {
return true;
} else {
return false;
}
}
/**************************************************************
* 포매팅 (input 값 포매팅)
* 전화번호(일반전화번호, 휴대전화번호, 대표번호) 자동 포매팅(-) 설정
* 파라미터 설명{
* target : 대상 Object
* }
****************************************************************/
const fn_fmt_telno = function (target) {
// 숫자만 허용
target.value = target.value.replace(/[^0-9]/g, '');
if (target.value.length <= 11) {
if (target.value.length == 11) {
// 휴대전화번호 혹은 02를 제외한 전화번호
target.value = target.value.replace(/(\d{3})(\d{4})(\d{4})/, '$1-$2-$3');
} else if (target.value.length == 9) {
// 02를 포함한 9자리 전화번호(02-000-0000)
target.value = target.value.replace(/(\d{2})(\d{3})(\d{4})/, '$1-$2-$3');
} else if (target.value.length == 8) {
// 대표전화번호 일 경우(0000-0000)
target.value = target.value.replace(/(\d{4})(\d{4})/, '$1-$2');
} else {
if (target.value.indexOf('02') == 0) {
// 02를 포함한 10자리 전화번호(02-0000-0000)
target.value = target.value.replace(/(\d{2})(\d{4})(\d{4})/, '$1-$2-$3');
} else {
// 02를 제외한 10자리 전화번호(032-000-0000)
target.value = target.value.replace(/(\d{3})(\d{3})(\d{4})/, '$1-$2-$3');
}
}
}
}
/* ********************************************************
* 주문 상세 이동
******************************************************** */
const fn_go_ordr_dtl = function () {
var accessUrl = document.getElementById('accessUrl').value;
if(accessUrl.startsWith("http://") && !(accessUrl.includes("dev.") || accessUrl.includes("loc."))){
accessUrl = accessUrl.replace("http://", "https://");
}
var form = document.getElementById('condForm');
form.setAttribute('method', 'post');
form.setAttribute('action', accessUrl+'/sopmal/main/selectNMbrOrdrDtlInqire');
form.submit();
}
/* ********************************************************
* 주문 번호 조회
******************************************************** */
const fn_select_oderNumbber = function() {
let sopMalCd = document.getElementById('sopMalCd').value;
let ordrInnb = document.getElementById('ordrInnb').value;
let mbtlnb = document.getElementById('mbtlnb').value;
let accessUrl = document.getElementById('accessUrl').value;
if (fn_is_empty(ordrInnb)) {
alert("주문번호를 입력해주세요.");
return false;
}
if (fn_is_empty(mbtlnb)) {
alert("주문 시 입력한 휴대폰번호를 입력해주세요.");
return false;
}
const form = document.getElementById('condForm');
const formData = new FormData(form); // 폼 데이터
fetch('/login/selectNMbrMbtlnbInqire', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
sopMalCd: sopMalCd,
ordrInnb: ordrInnb,
mbtlnb: mbtlnb,
accessUrl: accessUrl
})
})
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.json();
})
.then(data => {
// let result = data.resBody;
// let parsedResult = JSON.parse(result.result);
// let confirmAt = parsedResult.confirmAt;
let confirmAt = data.confirmAt;
if (confirmAt === 'Y') {
fn_go_ordr_dtl();
} else {
alert("휴대폰번호가 일치하지않습니다.");
}
})
.catch(error => {
alert("비회원 주문조회 실패");
console.error('Error:', error);
});
}
/* ********************************************************
* 체크박스 값변경
******************************************************** */
const fn_set_checkbox = function(_target) {
if ($(_target).is(':checked')) {
$(_target).val('Y');
} else {
$(_target).val('N');
}
}
/* ********************************************************
* 아이디 저장 기능 구현
******************************************************** */
document.addEventListener('DOMContentLoaded', function () {
const usernameInput = document.getElementById('username');
const rememberCheckbox = document.querySelector('input[type="checkbox"]');
const savedUsername = localStorage.getItem('savedUsername');
// 페이지 로드 시, 저장된 아이디가 있으면 입력 필드에 채워넣기
if (savedUsername) {
usernameInput.value = savedUsername;
rememberCheckbox.checked = true; // 체크박스도 체크 상태로 유지
}
// 체크박스 상태 변경 시 아이디 저장 또는 삭제
rememberCheckbox.addEventListener('change', function () {
if (this.checked) {
// 체크된 경우, 아이디를 localStorage에 저장
const username = usernameInput.value;
if (username) {
localStorage.setItem('savedUsername', username);
}
} else {
// 체크 해제된 경우, 저장된 아이디 삭제
localStorage.removeItem('savedUsername');
}
});
// 로그인 버튼 클릭 시 아이디 저장 처리
document.querySelector('.login__loginBtn').addEventListener('click', function () {
if (rememberCheckbox.checked) {
const username = usernameInput.value;
if (username) {
localStorage.setItem('savedUsername', username); // 아이디 저장
}
} else {
localStorage.removeItem('savedUsername'); // 체크 해제 시 아이디 삭제
}
});
//아이디 저장시 alert메시지 삭제
fn_hide_alertTxt('username', 'mbrIdAlertText');
});
</script>
<body>
<main>
<div class="sub-wrapper">
<div class="sub-content">
<div class="page-title center">
<h2 class="page-title__title">
<a th:href="${accessUrl}">
<img class="page-title__logo" th:src="@{${cdnurl}+${shopInfo.topFilCours}+'/'+${shopInfo.topSysFilNm}}" th:alt="${shopInfo.sopmalNm}">
</a>
<span class="visually-hidden">로그인</span>
</h2>
<th:block th:if="${purchasingAt == 'Y'}">
<p class="page-title__subTitle">회원으로 주문하시면 마일리지, 쿠폰, 할인 등 다양한 혜택을 받으실 수 있습니다.</p>
</th:block>
<th:block th:unless="${purchasingAt == 'Y'}">
<p class="page-title__subTitle">회원 로그인을 하시면 각종 혜택 및 정보를 제공 받으실 수 있습니다.</p>
</th:block>
</div>
<div class="login">
<div class="boxTab">
<button type="button" class="boxTab__tabBtn current">회원</button>
<button type="button" class="boxTab__tabBtn">비회원(주문조회)</button>
</div>
<div class="boxTab__content login-content current">
<form id="loginForm">
<input type="hidden" id="realm" th:value="${realm}">
<input type="hidden" id="redirectUri" th:value="${redirectUri}">
<input type="hidden" id="accessUrl" th:value="${accessUrl}">
<input type="hidden" id="sopMalCd" th:value="${sopMalCd}">
<div class="login__inputBox">
<input class="input-text full inputTarget" type="text" name="username" id="username" title="아이디"
placeholder="아이디" oninput="fn_hide_alertTxt('username','mbrIdAlertText');">
<p class="login__inputBox__alertText"><span id="mbrIdAlertText">아이디를 입력해 주세요.</span></p>
<input class="input-text full inputTarget" type="password" name="password" id="password" title="비밀번호"
placeholder="비밀번호" onkeyup="fn_hide_alertTxt('password','mbrPwAlertText',event);">
<p class="login__inputBox__alertText"><span id="mbrPwAlertText">비밀번호를 입력해 주세요.</span></p>
</div>
<button type="submit" class="btn xl full dark login__loginBtn">로그인</button>
</form>
<div class="login__misc">
<div class="b-checkbox-container">
<label class="b-checkbox is-medium checkbox">
<input type="checkbox" value="N" name="" onchange="fn_set_checkbox(this);">
<span class="check"></span>
<span class="control-label">아이디 저장</span>
</label>
</div>
<a class="login__misc__link" th:href="${accessUrl + '/sopmal/mbr/selectIdPasswordSearch'}">아이디/비밀번호 찾기</a>
</div>
<div class="login__signIn">
<p class="login__signIn__text">아직 <span th:text="${shopInfo.sopmalNm}"></span> 회원이 아니세요?</p>
<a class="login__signIn__link" th:href="${accessUrl + '/sopmal/mbr/selectJoinGuidance'}">회원가입</a>
</div>
<div class="login__sns">
<th:block th:if="${shopInfo.kakaoIntrlckAt == 'Y'}">
<button type="button" class="login__sns__btn kakao" onclick="openSocialLoginPopup('KAKAO')"><i class="icon icon_kakaoLogin"></i>카카오 로그인</button>
</th:block>
<th:block th:if="${shopInfo.naverIntrlckAt == 'Y'}">
<button type="button" class="login__sns__btn naver" onclick="openSocialLoginPopup('NAVER')"><i class="icon icon_naverLogin"></i>네이버 로그인</button>
</th:block>
</div>
<br>
<th:block th:if="${purchasingAt == 'Y'}">
<button class="btn xl full gray login__loginBtn" onclick="nmbrPurchsForm()">비회원으로 구매하기</button>
</th:block>
</div>
<div class="boxTab__content login-content">
<form id="condForm">
<div class="login__inputBox">
<input class="input-text full inputTarget" type="text" id="ordrInnb" name="ordrInnb" title="주문번호"
placeholder="주문번호" oninput="fn_hide_alertTxt('ordrInnb','nmbrOrdrAlertText');">
<p class="login__inputBox__alertText"><span id="nmbrOrdrAlertText">주문번호를 입력해 주세요.</span></p>
<input class="input-text number full inputTarget" type="tel" id="mbtlnb" name="mbtlnb" title="휴대폰번호"
placeholder="주문 시 입력한 휴대폰번호('-'없이)" oninput="fn_hide_alertTxt('mbtlnb','nmbrPwAlertText');">
<p class="login__inputBox__alertText"><span id="nmbrPwAlertText">휴대폰번호를 입력해 주세요.</span></p>
</div>
<button type="button" class="btn xl full dark" onclick="fn_select_oderNumbber();">조회</button>
</form>
</div>
</div>
</div>
</div>
</main>
<script>
/* ********************************************************
* 로그인
******************************************************** */
document.getElementById('loginForm').addEventListener('submit', function(e) {
e.preventDefault();
const realm = document.getElementById('realm').value;
const username = document.getElementById('username').value.replace(/\s+/g, '');
const password = document.getElementById('password').value.replace(/\s+/g, '');
const redirectUri = document.getElementById('redirectUri').value;
const accessUrl = document.getElementById('accessUrl').value;
const sopMalCd = document.getElementById('sopMalCd').value;
fetch(`/login/${realm}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
username: username,
password: password,
redirectUri: redirectUri,
accessUrl: accessUrl,
sopMalCd: sopMalCd
})
})
.then(response => {
console.log(response)
if (response.ok) {
return response;
}else{
switch(response.status){
case 401:
throw new Error('회원아이디나 비밀번호를 다시 확인해주세요.');
case 404:
break;
default:
throw new Error('회원 로그인에 실패하였습니다. 관리자에게 문의해주세요.');
}
}
})
.then(data => {
console.log('Login successful', data);
let moveTo = redirectUri;
if(redirectUri === '') moveTo = '/'
console.log('move', moveTo);
console.log('data.url', data.url);
window.location.href = data.url;
})
.catch(error => {
console.error('Error:', error);
alert(error.message);
});
});
/* ********************************************************
* 로그아웃
******************************************************** */
const logout = () => {
fetch('/logout', {
method: 'GET',
credentials: 'include'
}).then(response => {
if (response.ok) {
window.location.href = '/login';
} else {
console.error('Logout failed');
}
}).catch(error => {
console.error('Error:', error);
});
}
/* ********************************************************
* SNS 소셜 로그인
******************************************************** */
const openSocialLoginPopup = (provider) => {
var redirectUri = document.getElementById('redirectUri').value;
var sopMalCd = document.getElementById('sopMalCd').value;
var accessUrl = document.getElementById('accessUrl').value;
var tmpProvider = sopMalCd +'-'+ provider;
var width = 600;
var height = 600;
var left = (screen.width / 2) - (width / 2);
var top = (screen.height / 2) - (height / 2);
var url = '/oauth2/authorization/' + tmpProvider + '?access_url=' + encodeURIComponent(accessUrl) + '&redirect_uri=' + encodeURIComponent(redirectUri) + "&oauth2_type=login";
window.open(url, 'SocialLoginPopup', 'toolbar=no, location=no, directories=no, status=no, menubar=no, scrollbars=no, resizable=no, copyhistory=no, width=' + width + ', height=' + height + ', top=' + top + ', left=' + left);
}
const receiveMessage = (event) => {
if(event.origin !== window.location.origin) {
return;
}
if(event.data.type === 'LOGIN_SUCCESS') {
window.location.href = event.data.redirectUrl;
}
}
window.addEventListener('message', receiveMessage, false);
//비회원 주문 폼 이동
function nmbrPurchsForm(){
var accessUrl = document.getElementById('accessUrl').value;
window.location.href = accessUrl+'/sopmal/ordr/selectNmbrPurchsForm';
}
//로그인,주문조회 input 이벤트
function fn_hide_alertTxt(targetId, targetAlertId,event) {
const inputLen = document.querySelector("#" + targetId).value.length;
const target = document.querySelector("#" + targetAlertId);
if (inputLen > 0) {
if (targetId === 'password') {
if (event.getModifierState("CapsLock")) {
target.textContent = "Caps Lock이 켜져 있습니다.";
target.style.display = 'block';
} else {
target.style.display = 'none';
}
} else {
target.style.display = 'none';
}
} else {
if (targetId === 'password') {
target.textContent = "비밀번호를 입력해주세요.";
}
target.style.display = 'block'
}
}
</script>
</body>
</html>