kiosk 개발시작
This commit is contained in:
@@ -5,6 +5,7 @@ import java.util.HashMap;
|
|||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
import org.springframework.web.servlet.ModelAndView;
|
import org.springframework.web.servlet.ModelAndView;
|
||||||
|
|
||||||
@@ -34,7 +35,7 @@ public class KioskController extends ManagerDraftAction {
|
|||||||
/**
|
/**
|
||||||
* 키오스크 메인화면 화면으로 이동.
|
* 키오스크 메인화면 화면으로 이동.
|
||||||
*/
|
*/
|
||||||
@GetMapping("/kiosk/MainIntro.do")
|
@RequestMapping("/kiosk")
|
||||||
public ModelAndView selectMainIntro(HttpServletRequest request, HttpServletResponse response) {
|
public ModelAndView selectMainIntro(HttpServletRequest request, HttpServletResponse response) {
|
||||||
log.debug("KioskController selectMainIntro START");
|
log.debug("KioskController selectMainIntro START");
|
||||||
log.debug("KioskController selectMainIntro END");
|
log.debug("KioskController selectMainIntro END");
|
||||||
|
|||||||
548
src/main/resources/static/js/kiosk/new-patient.js
Normal file
548
src/main/resources/static/js/kiosk/new-patient.js
Normal file
@@ -0,0 +1,548 @@
|
|||||||
|
(function() {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
class NewPatientPage {
|
||||||
|
constructor() {
|
||||||
|
this.choicesInstances = new Map();
|
||||||
|
this.data = {
|
||||||
|
nationality: '',
|
||||||
|
nationalityCode: '',
|
||||||
|
userType: '',
|
||||||
|
visitPath: '',
|
||||||
|
treatment: ''
|
||||||
|
};
|
||||||
|
this.datepickerInstance = null;
|
||||||
|
this.isInitialized = false;
|
||||||
|
this.userModalRetryCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
init() {
|
||||||
|
if (this.isInitialized) return;
|
||||||
|
|
||||||
|
if (!this.validateDependencies()) {
|
||||||
|
this.scheduleRetry();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.validateDOM()) {
|
||||||
|
this.scheduleRetry();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.initializeComponents();
|
||||||
|
this.isInitialized = true;
|
||||||
|
console.log('✅ NewPatientPage 초기화 완료');
|
||||||
|
}
|
||||||
|
|
||||||
|
validateDependencies() {
|
||||||
|
return !!($ && window.Choices);
|
||||||
|
}
|
||||||
|
|
||||||
|
validateDOM() {
|
||||||
|
const requiredElements = [
|
||||||
|
'#selectNationality', '#selectUserType', '#selectTreatment',
|
||||||
|
'#selectChannel', '#selectIdentification', '#newPatientForm'
|
||||||
|
];
|
||||||
|
return requiredElements.every(selector => document.querySelector(selector));
|
||||||
|
}
|
||||||
|
|
||||||
|
scheduleRetry() {
|
||||||
|
setTimeout(() => this.init(), 150);
|
||||||
|
}
|
||||||
|
|
||||||
|
initializeComponents() {
|
||||||
|
this.initChoices();
|
||||||
|
this.initUserIntroModal(); // 내장 모달 초기화
|
||||||
|
this.loadCommonCategories();
|
||||||
|
this.initDatepicker();
|
||||||
|
this.bindEvents();
|
||||||
|
}
|
||||||
|
|
||||||
|
initChoices() {
|
||||||
|
const choicesConfig = {
|
||||||
|
searchEnabled: true,
|
||||||
|
searchPlaceholderValue: '검색...',
|
||||||
|
noResultsText: '결과 없음',
|
||||||
|
noChoicesText: '선택 가능한 항목이 없습니다',
|
||||||
|
itemSelectText: '선택',
|
||||||
|
removeItemButton: false,
|
||||||
|
shouldSort: false,
|
||||||
|
placeholderValue: '선택하세요'
|
||||||
|
};
|
||||||
|
|
||||||
|
const selectors = {
|
||||||
|
nationality: { id: '#selectNationality', key: 'nationality', placeholder: '국적 선택' },
|
||||||
|
userType: { id: '#selectUserType', key: 'userType', placeholder: '고객구분 선택' },
|
||||||
|
treatment: { id: '#selectTreatment', key: 'treatment', placeholder: '관심진료 선택' },
|
||||||
|
channel: { id: '#selectChannel', key: 'channel', placeholder: '방문경로 선택' },
|
||||||
|
identification: {
|
||||||
|
id: '#selectIdentification',
|
||||||
|
key: 'identification',
|
||||||
|
config: { searchEnabled: false, placeholder: false }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Object.entries(selectors).forEach(([key, config]) => {
|
||||||
|
try {
|
||||||
|
const instance = new Choices(config.id, {
|
||||||
|
...choicesConfig,
|
||||||
|
placeholder: true,
|
||||||
|
placeholderValue: config.placeholder,
|
||||||
|
...(config.config || {})
|
||||||
|
});
|
||||||
|
this.choicesInstances.set(config.key, instance);
|
||||||
|
} catch (e) {
|
||||||
|
console.error(`Choices 초기화 실패 (${key}):`, e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.toggleForeignerBox(false);
|
||||||
|
this.bindChoicesEvents();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 🔥 내장형 userIntroSelectModal
|
||||||
|
initUserIntroModal() {
|
||||||
|
window.userIntroSelectModal = {
|
||||||
|
callback: null,
|
||||||
|
dataList: null,
|
||||||
|
|
||||||
|
popup(callback) {
|
||||||
|
this.callback = callback;
|
||||||
|
this.initModal();
|
||||||
|
setTimeout(() => $('#userIntroSelectModal').modal('show'), 200);
|
||||||
|
},
|
||||||
|
|
||||||
|
initModal() {
|
||||||
|
if ($('#userIntroSelectModal').length) {
|
||||||
|
$('#userIntroSelectModal').remove();
|
||||||
|
}
|
||||||
|
$('body').append(this.htmlTemplate);
|
||||||
|
this.bindModalEvents();
|
||||||
|
},
|
||||||
|
|
||||||
|
bindModalEvents() {
|
||||||
|
$('#userIntroSelectModal .btnCancle').off().on('click', () => this.close());
|
||||||
|
$('#searchIntroUserBtn').off().on('click', () => this.search());
|
||||||
|
$('#introUserSearchKeyword').off('keypress').on('keypress', (e) => {
|
||||||
|
if (e.key === 'Enter') this.search();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
search() {
|
||||||
|
const keyword = $('#introUserSearchKeyword').val()?.trim();
|
||||||
|
if (keyword.length < 2) {
|
||||||
|
this.showAlert('검색어는 2자 이상 입력하세요.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url: '/kiosk/getUserOptionList.do',
|
||||||
|
method: 'POST',
|
||||||
|
data: { userSearchKeywordParam: keyword },
|
||||||
|
dataType: 'json',
|
||||||
|
success: (data) => {
|
||||||
|
if (data.msgCode === '0') {
|
||||||
|
this.dataList = data.rows || [];
|
||||||
|
this.renderResults(this.dataList);
|
||||||
|
} else {
|
||||||
|
this.showAlert('검색 결과가 없습니다.');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: () => this.showAlert('검색 중 오류가 발생했습니다.')
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
renderResults(users) {
|
||||||
|
const $tbody = $('#userIntroSelectModal tbody');
|
||||||
|
$tbody.empty();
|
||||||
|
|
||||||
|
if (users.length === 0) {
|
||||||
|
$tbody.html('<tr><td colspan="5" class="text-center p-3">검색결과가 없습니다.</td></tr>');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
users.forEach((user, index) => {
|
||||||
|
const $row = $(`
|
||||||
|
<tr class="user-row" data-index="${index}" style="cursor: pointer;">
|
||||||
|
<td>${user.username || ''}</td>
|
||||||
|
<td>${this.formatDate(user.birthday)}</td>
|
||||||
|
<td>${user.gender || ''}</td>
|
||||||
|
<td>${this.formatPhone(user.phonenumber)}</td>
|
||||||
|
<td>${user.lastvisitdate || ''}</td>
|
||||||
|
</tr>
|
||||||
|
`);
|
||||||
|
$tbody.append($row);
|
||||||
|
});
|
||||||
|
|
||||||
|
$('.user-row').off('click').on('click', (e) => {
|
||||||
|
const index = $(e.currentTarget).data('index');
|
||||||
|
this.selectUser(index);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
selectUser(index) {
|
||||||
|
if (this.dataList?.[index]) {
|
||||||
|
this.callback(this.dataList[index]);
|
||||||
|
}
|
||||||
|
this.close();
|
||||||
|
},
|
||||||
|
|
||||||
|
formatDate(dateStr) {
|
||||||
|
if (!dateStr) return '';
|
||||||
|
try {
|
||||||
|
const date = new Date(dateStr.match(/\d{4}-\d{2}-\d{2}/)?.[0]);
|
||||||
|
if (isNaN(date)) return '';
|
||||||
|
return date.toISOString().slice(0, 10).replace(/-/g, '.');
|
||||||
|
} catch {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
formatPhone(phone) {
|
||||||
|
if (!phone) return '';
|
||||||
|
return phone.replace(/(\d{3})(\d{4})(\d{4})/, '$1-$2-$3');
|
||||||
|
},
|
||||||
|
|
||||||
|
showAlert(message) {
|
||||||
|
if (window.modalEvent) {
|
||||||
|
window.modalEvent.warning('', message);
|
||||||
|
} else {
|
||||||
|
alert(message);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
close() {
|
||||||
|
$('#userIntroSelectModal').modal('hide');
|
||||||
|
setTimeout(() => $('#userIntroSelectModal').remove(), 500);
|
||||||
|
},
|
||||||
|
|
||||||
|
htmlTemplate: `
|
||||||
|
<div id="userIntroSelectModal" class="modal fade list1_diagnosis" tabindex="-1">
|
||||||
|
<div class="modal-dialog modal-lg">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header list1_modal_header">
|
||||||
|
<h5 class="modal-title">고객조회</h5>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body list1_modal_body">
|
||||||
|
<div class="input-group mb-3">
|
||||||
|
<div class="search_box flex-grow-1">
|
||||||
|
<img src="/image/web/search_B.svg" style="width:20px;margin-right:8px;">
|
||||||
|
<input id="introUserSearchKeyword" class="form-control" placeholder="성함, 연락처, 생년월일">
|
||||||
|
</div>
|
||||||
|
<button id="searchIntroUserBtn" class="btn btn-primary ms-2">검색</button>
|
||||||
|
</div>
|
||||||
|
<div class="list1_modal_table">
|
||||||
|
<table class="table table-sm">
|
||||||
|
<thead class="table-light">
|
||||||
|
<tr>
|
||||||
|
<th>성함</th><th>생년월일</th><th>성별</th><th>연락처</th><th>최근내원</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr><td colspan="5" class="text-center">검색어를 입력하세요.</td></tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer list1_modal_footer">
|
||||||
|
<button class="btn btn-secondary btnCancle">취소</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>`
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
bindChoicesEvents() {
|
||||||
|
const idSelect = document.getElementById('selectIdentification');
|
||||||
|
if (idSelect) {
|
||||||
|
idSelect.addEventListener('change', (e) => {
|
||||||
|
const isPassport = e.target.value === 'pno';
|
||||||
|
$('.passport_number_box').toggle(isPassport);
|
||||||
|
$('.foreigner_number_box').toggle(!isPassport);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const eventMap = {
|
||||||
|
'selectNationality': (e) => this.handleNationalityChange(e),
|
||||||
|
'selectUserType': (e) => this.data.userType = e.target.value,
|
||||||
|
'selectTreatment': (e) => this.data.treatment = e.target.value,
|
||||||
|
'selectChannel': (e) => this.handleChannelChange(e)
|
||||||
|
};
|
||||||
|
|
||||||
|
Object.entries(eventMap).forEach(([id, handler]) => {
|
||||||
|
const element = document.getElementById(id);
|
||||||
|
if (element) element.addEventListener('change', handler);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
handleNationalityChange(event) {
|
||||||
|
this.data.nationalityCode = event.target.value;
|
||||||
|
const selectedText = event.target.options[event.target.selectedIndex]?.text || '';
|
||||||
|
const isKorean = selectedText.includes('대한민국') || event.target.value.includes('KR') || event.target.value === 'Local';
|
||||||
|
this.data.nationality = isKorean ? 'Local' : 'Foreigner';
|
||||||
|
this.toggleForeignerBox(!isKorean);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleChannelChange(event) {
|
||||||
|
this.data.visitPath = event.target.value;
|
||||||
|
const selectedText = event.target.options[event.target.selectedIndex]?.text || '';
|
||||||
|
if (selectedText.includes('소개')) {
|
||||||
|
$('.searchIntroUser').show();
|
||||||
|
} else {
|
||||||
|
$('input[name="modalRecommendId"]').val('').removeAttr('data-userid');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleForeignerBox(show) {
|
||||||
|
$('.foreigner_box').toggle(show);
|
||||||
|
$('.local_box').toggle(!show);
|
||||||
|
}
|
||||||
|
|
||||||
|
loadCommonCategories() {
|
||||||
|
const categories = [
|
||||||
|
{ code: 'C202404110001', key: 'nationality' },
|
||||||
|
{ code: 'C202404110002', key: 'userType' },
|
||||||
|
{ code: 'C202404110003', key: 'channel' }
|
||||||
|
];
|
||||||
|
categories.forEach(({ code, key }) => this.fetchCategory(code, key));
|
||||||
|
this.fetchTreatmentList();
|
||||||
|
}
|
||||||
|
|
||||||
|
fetchCategory(code, choicesKey) {
|
||||||
|
$.ajax({
|
||||||
|
url: '/kiosk/getCategoryItem.do',
|
||||||
|
type: 'POST',
|
||||||
|
data: { categoryCode: code },
|
||||||
|
success: (data) => {
|
||||||
|
if (data.rows?.length > 0) {
|
||||||
|
const choices = data.rows.map(item => ({
|
||||||
|
value: item.categoryitemcode || item.commonCode || '',
|
||||||
|
label: item.categoryitemname || item.codeName || ''
|
||||||
|
}));
|
||||||
|
const instance = this.choicesInstances.get(choicesKey);
|
||||||
|
if (instance) {
|
||||||
|
instance.setChoices(choices, 'value', 'label', true);
|
||||||
|
if (choicesKey === 'nationality') {
|
||||||
|
const defaultItem = choices.find(c => c.label.includes('대한민국'));
|
||||||
|
if (defaultItem) {
|
||||||
|
instance.setChoiceByValue(defaultItem.value);
|
||||||
|
this.data.nationalityCode = defaultItem.value;
|
||||||
|
this.data.nationality = 'Local';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: (xhr) => console.error(`카테고리 조회 실패 (${code}):`, xhr.responseText)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fetchTreatmentList() {
|
||||||
|
$.ajax({
|
||||||
|
url: '/kiosk/getTreatmentOptionList.do',
|
||||||
|
type: 'POST',
|
||||||
|
success: (data) => {
|
||||||
|
if (data.rows?.length > 0) {
|
||||||
|
const choices = data.rows.map(item => ({
|
||||||
|
value: item.mutreatmentid || '',
|
||||||
|
label: item.treatmentname || ''
|
||||||
|
}));
|
||||||
|
const instance = this.choicesInstances.get('treatment');
|
||||||
|
if (instance) instance.setChoices(choices, 'value', 'label', true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
initDatepicker() {
|
||||||
|
const birthdayInput = document.querySelector('input[name="modalBirthday"]');
|
||||||
|
if (!birthdayInput) return;
|
||||||
|
|
||||||
|
this.datepickerInstance = flatpickr(birthdayInput, {
|
||||||
|
locale: "ko",
|
||||||
|
dateFormat: "Y-m-d",
|
||||||
|
maxDate: "today",
|
||||||
|
disableMobile: true,
|
||||||
|
onChange: (selectedDates) => {
|
||||||
|
if (selectedDates.length > 0) {
|
||||||
|
this.calculateAgeAndRrn(selectedDates[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$('.date-input-wrapper').off('click.datepicker').on('click.datepicker', (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
this.datepickerInstance?.open();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
calculateAgeAndRrn(birthDate) {
|
||||||
|
const today = new Date();
|
||||||
|
let age = today.getFullYear() - birthDate.getFullYear();
|
||||||
|
const m = today.getMonth() - birthDate.getMonth();
|
||||||
|
if (m < 0 || (m === 0 && today.getDate() < birthDate.getDate())) age--;
|
||||||
|
$('.txtAge').text(`만 ${age}세`);
|
||||||
|
|
||||||
|
const yy = String(birthDate.getFullYear()).substring(2);
|
||||||
|
const mm = String(birthDate.getMonth() + 1).padStart(2, '0');
|
||||||
|
const dd = String(birthDate.getDate()).padStart(2, '0');
|
||||||
|
$('input[name="modalUserRrn1"]').val(`${yy}${mm}${dd}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
bindEvents() {
|
||||||
|
$('.cancel_btn').off('click.newPatient').on('click.newPatient', () => history.back());
|
||||||
|
$('.registration_bth').off('click.newPatient').on('click.newPatient', () => this.save());
|
||||||
|
|
||||||
|
$('input[name="modalPhoneNumber"], input[name="modalPhoneNumber2"]')
|
||||||
|
.off('input.phoneFormat').on('input.phoneFormat', function() {
|
||||||
|
this.value = this.value.replace(/[^0-9]/g, '')
|
||||||
|
.replace(/(\d{3})(\d{3,4})(\d{4})/, '$1-$2-$3').substring(0, 13);
|
||||||
|
});
|
||||||
|
|
||||||
|
$('.searchIntroUser').off('click.newPatient').on('click.newPatient', () => {
|
||||||
|
window.userIntroSelectModal?.popup((obj) => {
|
||||||
|
$('input[name="modalRecommendId"]').val(obj.username || '').attr('data-userid', obj.muuserid || '');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async save() {
|
||||||
|
const formData = this.collectFormData();
|
||||||
|
if (!this.validateForm(formData)) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await this.submitForm(formData);
|
||||||
|
this.handleSaveResponse(response);
|
||||||
|
} catch (error) {
|
||||||
|
this.showError('통신 중 오류가 발생했습니다.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
collectFormData() {
|
||||||
|
return {
|
||||||
|
userName: $('input[name="modalUserName"]').val()?.trim() || '',
|
||||||
|
phone: $('input[name="modalPhoneNumber"]').val().replace(/-/g, '') || '',
|
||||||
|
phone2: $('input[name="modalPhoneNumber2"]').val().replace(/-/g, '') || '',
|
||||||
|
birthday: $('input[name="modalBirthday"]').val() || '',
|
||||||
|
gender: $('input[name="modalGender"]:checked').val() || '',
|
||||||
|
smsYn: $('input[name="modalSmsYn"]:checked').val() || '',
|
||||||
|
refusePhoto: $('input[name="modalRefusePhotoYn"]:checked').val() || '',
|
||||||
|
rrn1: $('input[name="modalUserRrn1"]').val() || '',
|
||||||
|
rrn2: $('input[name="modalUserRrn2"]').val() || '',
|
||||||
|
pno: $('input[name="modalUserPno"]').val() || '',
|
||||||
|
arc1: $('input[name="modalUserArc1"]').val() || '',
|
||||||
|
arc2: $('input[name="modalUserArc2"]').val() || '',
|
||||||
|
memo: $('textarea[name="modalMemo"]').val() || '',
|
||||||
|
etc: $('textarea[name="modalEtc"]').val() || '',
|
||||||
|
introUserId: $('input[name="modalRecommendId"]').attr('data-userid') || '',
|
||||||
|
...this.data
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
validateForm(data) {
|
||||||
|
const errors = {
|
||||||
|
userName: !data.userName && '고객명을 입력해주세요.',
|
||||||
|
phone: (!data.phone || data.phone.length < 10) && '올바른 연락처를 입력해주세요.',
|
||||||
|
privacy: !$('#agree_privacy').is(':checked') && '개인정보 수집 이용에 동의해야 합니다.'
|
||||||
|
};
|
||||||
|
|
||||||
|
const errorMsg = Object.values(errors).find(Boolean);
|
||||||
|
if (errorMsg && window.modalEvent) {
|
||||||
|
window.modalEvent.warning('알림', errorMsg);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
submitForm(data) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
$.ajax({
|
||||||
|
url: '/kiosk/putUser.do',
|
||||||
|
type: 'POST',
|
||||||
|
data: {
|
||||||
|
nationality: data.nationality,
|
||||||
|
nationalityCode: data.nationalityCode,
|
||||||
|
userName: data.userName,
|
||||||
|
phoneNumber: data.phone,
|
||||||
|
phoneNumber2: data.phone2,
|
||||||
|
birthday: data.birthday,
|
||||||
|
gender: data.gender,
|
||||||
|
userRrn1: data.rrn1,
|
||||||
|
userRrn2: data.rrn2,
|
||||||
|
userPno: data.pno,
|
||||||
|
userArc1: data.arc1,
|
||||||
|
userArc2: data.arc2,
|
||||||
|
userTypeCode: data.userType,
|
||||||
|
channelCode: data.visitPath,
|
||||||
|
muGroupId: data.treatment,
|
||||||
|
introUserId: data.introUserId,
|
||||||
|
memo: data.memo,
|
||||||
|
etc: data.etc,
|
||||||
|
smsYn: data.smsYn,
|
||||||
|
refusePhotoYn: data.refusePhoto
|
||||||
|
},
|
||||||
|
success: resolve,
|
||||||
|
error: reject
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
handleSaveResponse(data) {
|
||||||
|
if (data.msgCode === '0') {
|
||||||
|
if (window.modalEvent) {
|
||||||
|
window.modalEvent.success('등록 성공', '신규 고객 등록이 완료되었습니다.', () => {
|
||||||
|
location.href = '/kiosk/kiosk_main';
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
location.href = '/kiosk/kiosk_main';
|
||||||
|
}
|
||||||
|
} else if (window.modalEvent) {
|
||||||
|
window.modalEvent.danger('오류', data.msgDesc || '등록에 실패했습니다.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
showError(message) {
|
||||||
|
if (window.modalEvent) {
|
||||||
|
window.modalEvent.danger('오류', message);
|
||||||
|
} else {
|
||||||
|
alert(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
destroy() {
|
||||||
|
this.choicesInstances.forEach(instance => instance?.destroy());
|
||||||
|
this.choicesInstances.clear();
|
||||||
|
this.datepickerInstance?.destroy();
|
||||||
|
$('#userIntroSelectModal')?.remove();
|
||||||
|
this.isInitialized = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 싱글톤 인스턴스 관리
|
||||||
|
let instance = null;
|
||||||
|
window.newPatientPage = {
|
||||||
|
init() {
|
||||||
|
if (instance) return;
|
||||||
|
instance = new NewPatientPage();
|
||||||
|
instance.init();
|
||||||
|
},
|
||||||
|
destroy() {
|
||||||
|
instance?.destroy();
|
||||||
|
instance = null;
|
||||||
|
},
|
||||||
|
getInstance() {
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 자동 초기화
|
||||||
|
const init = () => window.newPatientPage.init();
|
||||||
|
|
||||||
|
if (document.readyState === 'loading') {
|
||||||
|
document.addEventListener('DOMContentLoaded', init);
|
||||||
|
} else {
|
||||||
|
setTimeout(init, 50);
|
||||||
|
}
|
||||||
|
window.addEventListener('load', () => setTimeout(init, 100));
|
||||||
|
})();
|
||||||
@@ -26,9 +26,9 @@
|
|||||||
</head>
|
</head>
|
||||||
|
|
||||||
<th:block layout:fragment="layout_top_script">
|
<th:block layout:fragment="layout_top_script">
|
||||||
<script src="/js/kiosk/common.js"></script>
|
<script src="/js/kiosk/new-patient.js"></script>
|
||||||
</th:block>
|
</th:block>
|
||||||
<th:block th:replace="/web/include/modal :: layout_modal"></th:block>
|
<!--<th:block th:replace="/web/include/modal :: layout_modal"></th:block>-->
|
||||||
|
|
||||||
<th:block layout:fragment="layout_content">
|
<th:block layout:fragment="layout_content">
|
||||||
<div id="newPatientForm" class="new-patient-container">
|
<div id="newPatientForm" class="new-patient-container">
|
||||||
@@ -223,328 +223,9 @@
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
</th:block>
|
</th:block>
|
||||||
<th:block layout:fragment="layout_popup">
|
|
||||||
</th:block>
|
|
||||||
<th:block layout:fragment="layout_script">
|
<th:block layout:fragment="layout_script">
|
||||||
<script type="text/javascript">
|
|
||||||
// Choices.js 인스턴스 저장
|
|
||||||
let choicesInstances = {};
|
|
||||||
|
|
||||||
$(document).ready(function () {
|
|
||||||
newPatientPage.init();
|
|
||||||
});
|
|
||||||
|
|
||||||
let newPatientPage = {
|
|
||||||
data: {
|
|
||||||
nationality: '',
|
|
||||||
nationalityCode: '',
|
|
||||||
userType: '',
|
|
||||||
visitPath: '',
|
|
||||||
treatment: ''
|
|
||||||
},
|
|
||||||
|
|
||||||
init: function () {
|
|
||||||
this.initChoices();
|
|
||||||
this.loadCommonCategories();
|
|
||||||
this.initDatepicker();
|
|
||||||
this.setEvent();
|
|
||||||
},
|
|
||||||
|
|
||||||
// Choices.js 초기화
|
|
||||||
initChoices: function () {
|
|
||||||
const choicesConfig = {
|
|
||||||
searchEnabled: true,
|
|
||||||
searchPlaceholderValue: '검색...',
|
|
||||||
noResultsText: '결과 없음',
|
|
||||||
noChoicesText: '선택 가능한 항목이 없습니다',
|
|
||||||
itemSelectText: '선택',
|
|
||||||
removeItemButton: false,
|
|
||||||
shouldSort: false,
|
|
||||||
placeholderValue: '선택하세요'
|
|
||||||
};
|
|
||||||
|
|
||||||
// 국적
|
|
||||||
choicesInstances.nationality = new Choices('#selectNationality', {
|
|
||||||
...choicesConfig,
|
|
||||||
placeholder: true,
|
|
||||||
placeholderValue: '국적 선택'
|
|
||||||
});
|
|
||||||
|
|
||||||
// 고객구분
|
|
||||||
choicesInstances.userType = new Choices('#selectUserType', {
|
|
||||||
...choicesConfig,
|
|
||||||
placeholder: true,
|
|
||||||
placeholderValue: '고객구분 선택'
|
|
||||||
});
|
|
||||||
|
|
||||||
// 관심진료
|
|
||||||
choicesInstances.treatment = new Choices('#selectTreatment', {
|
|
||||||
...choicesConfig,
|
|
||||||
placeholder: true,
|
|
||||||
placeholderValue: '관심진료 선택'
|
|
||||||
});
|
|
||||||
|
|
||||||
// 방문경로
|
|
||||||
choicesInstances.channel = new Choices('#selectChannel', {
|
|
||||||
...choicesConfig,
|
|
||||||
placeholder: true,
|
|
||||||
placeholderValue: '방문경로 선택'
|
|
||||||
});
|
|
||||||
|
|
||||||
// 식별정보 (여권/외국인등록번호)
|
|
||||||
choicesInstances.identification = new Choices('#selectIdentification', {
|
|
||||||
...choicesConfig,
|
|
||||||
searchEnabled: false,
|
|
||||||
placeholder: false
|
|
||||||
});
|
|
||||||
|
|
||||||
// 초기 상태 설정 (한국인 기본)
|
|
||||||
$('.foreigner_box').hide();
|
|
||||||
|
|
||||||
// Change 이벤트 바인딩
|
|
||||||
this.bindChoicesEvents();
|
|
||||||
},
|
|
||||||
|
|
||||||
bindChoicesEvents: function () {
|
|
||||||
let self = this;
|
|
||||||
|
|
||||||
// 식별정보 변경
|
|
||||||
let idSelect = document.getElementById('selectIdentification');
|
|
||||||
if (idSelect) {
|
|
||||||
idSelect.addEventListener('change', function (e) {
|
|
||||||
if (this.value === 'pno') {
|
|
||||||
$('.passport_number_box').show();
|
|
||||||
$('.foreigner_number_box').hide();
|
|
||||||
} else {
|
|
||||||
$('.passport_number_box').hide();
|
|
||||||
$('.foreigner_number_box').show();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 국적 변경
|
|
||||||
document.getElementById('selectNationality').addEventListener('change', function (e) {
|
|
||||||
self.data.nationalityCode = this.value;
|
|
||||||
let selectedText = this.options[this.selectedIndex]?.text || '';
|
|
||||||
|
|
||||||
if (selectedText.includes('대한민국') || this.value.includes('KR') || this.value === 'Local') {
|
|
||||||
self.data.nationality = 'Local';
|
|
||||||
$('.local_box').show();
|
|
||||||
$('.foreigner_box').hide();
|
|
||||||
} else {
|
|
||||||
self.data.nationality = 'Foreigner';
|
|
||||||
$('.local_box').hide();
|
|
||||||
$('.foreigner_box').show();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// 고객구분 변경
|
|
||||||
document.getElementById('selectUserType').addEventListener('change', function (e) {
|
|
||||||
self.data.userType = this.value;
|
|
||||||
});
|
|
||||||
|
|
||||||
// 관심진료 변경
|
|
||||||
document.getElementById('selectTreatment').addEventListener('change', function (e) {
|
|
||||||
self.data.treatment = this.value;
|
|
||||||
});
|
|
||||||
|
|
||||||
// 방문경로 변경
|
|
||||||
document.getElementById('selectChannel').addEventListener('change', function (e) {
|
|
||||||
self.data.visitPath = this.value;
|
|
||||||
let selectedText = this.options[this.selectedIndex]?.text || '';
|
|
||||||
|
|
||||||
if (selectedText.includes('소개')) {
|
|
||||||
$('.searchIntroUser').show();
|
|
||||||
} else {
|
|
||||||
$('input[name="modalRecommendId"]').val('').removeAttr('data-userid');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
// 공통 코드 로드
|
|
||||||
loadCommonCategories: function () {
|
|
||||||
this.fetchCategory('C202404110001', 'nationality'); // 국적
|
|
||||||
this.fetchCategory('C202404110002', 'userType'); // 고객구분
|
|
||||||
this.fetchCategory('C202404110003', 'channel'); // 방문경로
|
|
||||||
this.fetchTreatmentList(); // 관심진료
|
|
||||||
},
|
|
||||||
|
|
||||||
fetchCategory: function (code, choicesKey) {
|
|
||||||
$.ajax({
|
|
||||||
url: '/kiosk/getCategoryItem.do',
|
|
||||||
type: 'POST',
|
|
||||||
data: { categoryCode: code },
|
|
||||||
success: function (data) {
|
|
||||||
if (data.rows && data.rows.length > 0) {
|
|
||||||
let choices = data.rows.map(item => ({
|
|
||||||
value: item.categoryitemcode || item.commonCode || '',
|
|
||||||
label: item.categoryitemname || item.codeName || ''
|
|
||||||
}));
|
|
||||||
|
|
||||||
// Choices 인스턴스에 옵션 설정
|
|
||||||
if (choicesInstances[choicesKey]) {
|
|
||||||
choicesInstances[choicesKey].setChoices(choices, 'value', 'label', true);
|
|
||||||
|
|
||||||
// 국적의 경우 기본값 설정 (대한민국)
|
|
||||||
if (choicesKey === 'nationality') {
|
|
||||||
let defaultItem = choices.find(c => c.label.includes('대한민국'));
|
|
||||||
if (defaultItem) {
|
|
||||||
choicesInstances[choicesKey].setChoiceByValue(defaultItem.value);
|
|
||||||
newPatientPage.data.nationalityCode = defaultItem.value;
|
|
||||||
newPatientPage.data.nationality = 'Local';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
error: function (xhr) {
|
|
||||||
console.error("카테고리 조회 실패:", code, xhr.responseText);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
fetchTreatmentList: function () {
|
|
||||||
$.ajax({
|
|
||||||
url: '/kiosk/getTreatmentOptionList.do',
|
|
||||||
type: 'POST',
|
|
||||||
success: function (data) {
|
|
||||||
if (data.rows && data.rows.length > 0) {
|
|
||||||
let choices = data.rows.map(item => ({
|
|
||||||
value: item.mutreatmentid || '',
|
|
||||||
label: item.treatmentname || ''
|
|
||||||
}));
|
|
||||||
|
|
||||||
if (choicesInstances.treatment) {
|
|
||||||
choicesInstances.treatment.setChoices(choices, 'value', 'label', true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
// Datepicker 초기화 (Flatpickr)
|
|
||||||
initDatepicker: function () {
|
|
||||||
let birthdayInput = document.querySelector('input[name="modalBirthday"]');
|
|
||||||
if (!birthdayInput) return;
|
|
||||||
|
|
||||||
let fp = flatpickr(birthdayInput, {
|
|
||||||
locale: "ko",
|
|
||||||
dateFormat: "Y-m-d",
|
|
||||||
maxDate: "today",
|
|
||||||
disableMobile: true,
|
|
||||||
onChange: function (selectedDates, dateStr, instance) {
|
|
||||||
if (selectedDates.length > 0) {
|
|
||||||
let birthDate = selectedDates[0];
|
|
||||||
let today = new Date();
|
|
||||||
let age = today.getFullYear() - birthDate.getFullYear();
|
|
||||||
|
|
||||||
// 만 나이 계산
|
|
||||||
let m = today.getMonth() - birthDate.getMonth();
|
|
||||||
if (m < 0 || (m === 0 && today.getDate() < birthDate.getDate())) {
|
|
||||||
age--;
|
|
||||||
}
|
|
||||||
|
|
||||||
$('.txtAge').text('만 ' + age + '세');
|
|
||||||
|
|
||||||
// 주민번호 앞자리 자동 완성 (YYMMDD)
|
|
||||||
let yy = String(birthDate.getFullYear()).substring(2);
|
|
||||||
let mm = String(birthDate.getMonth() + 1).padStart(2, '0');
|
|
||||||
let dd = String(birthDate.getDate()).padStart(2, '0');
|
|
||||||
$('input[name="modalUserRrn1"]').val(yy + mm + dd);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// 래퍼/아이콘 클릭 시 달력 열기
|
|
||||||
$('.date-input-wrapper').off('click').on('click', function (e) {
|
|
||||||
e.stopPropagation();
|
|
||||||
fp.open();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
// 이벤트 설정
|
|
||||||
setEvent: function () {
|
|
||||||
$('.cancel_btn').off('click').on("click", function () { history.back(); });
|
|
||||||
$('.registration_bth').off('click').on("click", function () { newPatientPage.save(); });
|
|
||||||
|
|
||||||
// 추천인 검색
|
|
||||||
$('.searchIntroUser').off('click').on('click', function () {
|
|
||||||
if (typeof userIntroSelectModal !== 'undefined') {
|
|
||||||
userIntroSelectModal.popup(function (obj) {
|
|
||||||
$('input[name="modalRecommendId"]').val(obj.username).attr('data-userid', obj.muuserid);
|
|
||||||
}, {});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// 전화번호 포맷
|
|
||||||
$('input[name="modalPhoneNumber"], input[name="modalPhoneNumber2"]').on('input', function () {
|
|
||||||
this.value = this.value.replace(/[^0-9]/g, '').replace(/(\d{3})(\d{3,4})(\d{4})/, '$1-$2-$3').substring(0, 13);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
save: function () {
|
|
||||||
let userName = $('input[name="modalUserName"]').val().trim();
|
|
||||||
let phone = $('input[name="modalPhoneNumber"]').val().replace(/-/g, '');
|
|
||||||
let phone2 = $('input[name="modalPhoneNumber2"]').val().replace(/-/g, '');
|
|
||||||
let birthday = $('input[name="modalBirthday"]').val();
|
|
||||||
let gender = $('input[name="modalGender"]:checked').val();
|
|
||||||
let smsYn = $('input[name="modalSmsYn"]:checked').val();
|
|
||||||
let refusePhoto = $('input[name="modalRefusePhotoYn"]:checked').val();
|
|
||||||
let rrn1 = $('input[name="modalUserRrn1"]').val();
|
|
||||||
let rrn2 = $('input[name="modalUserRrn2"]').val();
|
|
||||||
let pno = $('input[name="modalUserPno"]').val();
|
|
||||||
let arc1 = $('input[name="modalUserArc1"]').val();
|
|
||||||
let arc2 = $('input[name="modalUserArc2"]').val();
|
|
||||||
let memo = $('textarea[name="modalMemo"]').val();
|
|
||||||
let etc = $('textarea[name="modalEtc"]').val();
|
|
||||||
let introUserId = $('input[name="modalRecommendId"]').attr('data-userid') || '';
|
|
||||||
|
|
||||||
// 유효성 검사
|
|
||||||
if (!userName) { modalEvent.warning("알림", "고객명을 입력해주세요."); return; }
|
|
||||||
if (!phone || phone.length < 10) { modalEvent.warning("알림", "올바른 연락처를 입력해주세요."); return; }
|
|
||||||
if (!$('#agree_privacy').is(':checked')) { modalEvent.warning("알림", "개인정보 수집 이용에 동의해야 합니다."); return; }
|
|
||||||
|
|
||||||
$.ajax({
|
|
||||||
url: '/kiosk/putUser.do',
|
|
||||||
type: 'POST',
|
|
||||||
data: {
|
|
||||||
nationality: this.data.nationality,
|
|
||||||
nationalityCode: this.data.nationalityCode,
|
|
||||||
userName: userName,
|
|
||||||
phoneNumber: phone,
|
|
||||||
phoneNumber2: phone2,
|
|
||||||
birthday: birthday,
|
|
||||||
gender: gender,
|
|
||||||
userRrn1: rrn1,
|
|
||||||
userRrn2: rrn2,
|
|
||||||
userPno: pno,
|
|
||||||
userArc1: arc1,
|
|
||||||
userArc2: arc2,
|
|
||||||
userTypeCode: this.data.userType,
|
|
||||||
channelCode: this.data.visitPath,
|
|
||||||
muGroupId: this.data.treatment,
|
|
||||||
introUserId: introUserId,
|
|
||||||
memo: memo,
|
|
||||||
etc: etc,
|
|
||||||
smsYn: smsYn,
|
|
||||||
refusePhotoYn: refusePhoto
|
|
||||||
},
|
|
||||||
success: function (data) {
|
|
||||||
if (data.msgCode == '0') {
|
|
||||||
modalEvent.success("등록 성공", "신규 고객 등록이 완료되었습니다.", function () {
|
|
||||||
location.href = '/kiosk/kiosk_main';
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
modalEvent.danger("오류", data.msgDesc);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
error: function () {
|
|
||||||
modalEvent.danger("오류", "통신 중 오류가 발생했습니다.");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
</th:block>
|
</th:block>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
Reference in New Issue
Block a user