2026-01-25 작업

This commit is contained in:
pjs
2026-01-25 21:54:15 +09:00
parent a7d2fa31e7
commit 50d2f05404
12 changed files with 399 additions and 446 deletions

View File

@@ -30,7 +30,11 @@ public class DatabaseConfig {
factoryBean.setDataSource(dataSource); factoryBean.setDataSource(dataSource);
factoryBean.setMapperLocations(applicationContext.getResources("classpath:/mappers/**/*.xml")); factoryBean.setMapperLocations(applicationContext.getResources("classpath:/mappers/**/*.xml"));
return factoryBean.getObject(); org.apache.ibatis.session.Configuration mybatisConfig = new org.apache.ibatis.session.Configuration();
mybatisConfig.setMapUnderscoreToCamelCase(true); // 언더바를 카멜케이스로 매핑
factoryBean.setConfiguration(mybatisConfig);
return factoryBean.getObject();
} }
@Bean @Bean

View File

@@ -37,6 +37,8 @@ public interface Constants {
static final public String END_DATE_NONE = "408"; // 서비스 종료일 정보가 없습니다. static final public String END_DATE_NONE = "408"; // 서비스 종료일 정보가 없습니다.
static final public String USER_SAME = "409"; // 이미 등록되어 있는 사용자입니다. static final public String USER_SAME = "409"; // 이미 등록되어 있는 사용자입니다.
static final public String EBS_ID_UNEQUAL = "410"; // 사용자 정보가 없습니다. static final public String EBS_ID_UNEQUAL = "410"; // 사용자 정보가 없습니다.
static final public String SERVER_ERROR = "서버 오류가 발생했습니다."; //서버 오류가 발생했습니다.
} }

View File

@@ -1,6 +1,7 @@
package com.madeu.crm.kiosk.ctrl; package com.madeu.crm.kiosk.ctrl;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
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;
@@ -12,6 +13,7 @@ import org.springframework.web.servlet.ModelAndView;
import com.madeu.constants.Constants; import com.madeu.constants.Constants;
import com.madeu.crm.kiosk.dto.ConsentFormDTO; import com.madeu.crm.kiosk.dto.ConsentFormDTO;
import com.madeu.crm.kiosk.dto.KioskDTO;
import com.madeu.crm.kiosk.dto.UserDTO; import com.madeu.crm.kiosk.dto.UserDTO;
import com.madeu.crm.kiosk.service.KioskService; import com.madeu.crm.kiosk.service.KioskService;
import com.madeu.init.ManagerDraftAction; import com.madeu.init.ManagerDraftAction;
@@ -224,7 +226,7 @@ public class KioskController extends ManagerDraftAction {
} catch (Exception e) { } catch (Exception e) {
log.error("Error inserting user", e); log.error("Error inserting user", e);
resultMap.put("msgCode", Constants.FAIL); resultMap.put("msgCode", Constants.FAIL);
resultMap.put("msgDesc", "서버 오류가 발생했습니다."); resultMap.put("msgDesc", Constants.SERVER_ERROR);
errorMsg.append(e.getMessage()); errorMsg.append(e.getMessage());
} finally { } finally {
// 결과 처리 및 로그 기록 (기존과 동일) // 결과 처리 및 로그 기록 (기존과 동일)
@@ -233,6 +235,26 @@ public class KioskController extends ManagerDraftAction {
return resultMap; return resultMap;
} }
/**
* 고객 등록
*/
@PostMapping("/kiosk/putSubmit.do")
public KioskDTO putSubmit(@RequestBody KioskDTO dto) {
return null;
}
/**
* 고객 등록
* @throws Exception
*/
@PostMapping("/kiosk/getUserByPhoneNumber.do")
public HashMap<String, Object> getUserByPhoneNumber(@RequestBody KioskDTO dto) throws Exception {
return kioskService.getUserByPhoneNumber(dto);
}
/** /**
* 고객 정보 조회 * 고객 정보 조회

View File

@@ -0,0 +1,19 @@
package com.madeu.crm.kiosk.dto;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
@Getter
@Setter
@ToString
public class KioskDTO {
private String userNumber; // 고객명
private String userName; // 고객명
private String birthday; // 생년월일 (YYYYMMDD)
private String gender; // 성별 (M/F)
private String phoneNumber;
private String lastVisitDate;
private String msgCode;
private String msgDesc;
}

View File

@@ -6,6 +6,7 @@ import java.util.Map;
import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Mapper;
import com.madeu.crm.kiosk.dto.ConsentFormDTO; import com.madeu.crm.kiosk.dto.ConsentFormDTO;
import com.madeu.crm.kiosk.dto.KioskDTO;
import com.madeu.crm.kiosk.dto.UserDTO; import com.madeu.crm.kiosk.dto.UserDTO;
@Mapper @Mapper
@@ -47,4 +48,6 @@ public interface KioskMAP {
List<Map<String, Object>> getReserveUserOptionList(Map<String, Object> paramMap); List<Map<String, Object>> getReserveUserOptionList(Map<String, Object> paramMap);
int insertUser(UserDTO dto); int insertUser(UserDTO dto);
List<KioskDTO> selectUserByPhoneNumber(KioskDTO dto);
} }

View File

@@ -12,6 +12,7 @@ import org.springframework.stereotype.Service;
import com.madeu.constants.Constants; import com.madeu.constants.Constants;
import com.madeu.crm.kiosk.dto.ConsentFormDTO; import com.madeu.crm.kiosk.dto.ConsentFormDTO;
import com.madeu.crm.kiosk.dto.KioskDTO;
import com.madeu.crm.kiosk.dto.UserDTO; import com.madeu.crm.kiosk.dto.UserDTO;
import com.madeu.crm.kiosk.map.KioskMAP; import com.madeu.crm.kiosk.map.KioskMAP;
import com.madeu.dao.web.webuser.WebUserSqlMapDAO; import com.madeu.dao.web.webuser.WebUserSqlMapDAO;
@@ -312,4 +313,28 @@ public class KioskService {
public ConsentFormDTO getConsentForm(ConsentFormDTO dto) { public ConsentFormDTO getConsentForm(ConsentFormDTO dto) {
return kioskMAP.selectConsentForm(dto); return kioskMAP.selectConsentForm(dto);
} }
public HashMap<String, Object> getUserByPhoneNumber(KioskDTO dto) throws Exception {
HashMap<String, Object> map = new HashMap<>();
List<KioskDTO> list = null;
try {
list = kioskMAP.selectUserByPhoneNumber(dto);
map.put("list", list);
map.put("msgCode", Constants.OK);
//dto.setMsgDesc(Constants.SERVER_ERROR);
} catch (Exception e) {
map.put("msgCode", Constants.FAIL);
map.put("msgDesc", Constants.SERVER_ERROR);
e.printStackTrace();
}
return map;
}
} }

View File

@@ -16,11 +16,6 @@ spring:
aop: aop:
proxy-target-class: false proxy-target-class: false
mybatis:
mapper-locations: mybatis/mappers/*.xml
configuration:
map-underscore-to-camel-case: true
server: server:
encoding: encoding:
charset: UTF-8 charset: UTF-8

View File

@@ -438,6 +438,7 @@
END AS "appUseYn" END AS "appUseYn"
,DATE_FORMAT(MU.APP_INTRO_DATE, '%Y-%m-%d') AS "appIntroDate" ,DATE_FORMAT(MU.APP_INTRO_DATE, '%Y-%m-%d') AS "appIntroDate"
,DATE_FORMAT(MU.LEAVE_DATE, '%Y-%m-%d') AS "leaveDate" ,DATE_FORMAT(MU.LEAVE_DATE, '%Y-%m-%d') AS "leaveDate"
,INTRO_NAME as "introName"
FROM MU_USER AS MU FROM MU_USER AS MU
LEFT JOIN MU_CATEGORY_ITEM AS MCIN LEFT JOIN MU_CATEGORY_ITEM AS MCIN
ON MCIN.MU_CATEGORY_ITEM_ID = MU.NATIONALITY ON MCIN.MU_CATEGORY_ITEM_ID = MU.NATIONALITY

View File

@@ -364,6 +364,27 @@
AND MU.MU_USER_ID = #{muUserId} AND MU.MU_USER_ID = #{muUserId}
</select> </select>
<select id="selectUserByPhoneNumber" parameterType="com.madeu.crm.kiosk.dto.KioskDTO" resultType="com.madeu.crm.kiosk.dto.KioskDTO">
SELECT
MU_USER_ID,
USER_NUMBER,
USER_NAME,
BIRTHDAY,
GENDER,
PHONE_NUMBER,
USER_TYPE,
LAST_VISIT_DATE,
ETC,
MEMO
FROM MU_USER
WHERE USE_YN = 'Y'
AND PHONE_NUMBER = #{phoneNumber}
ORDER BY USER_NUMBER DESC
</select>
<select id="getHospital" parameterType="hashmap" resultType="hashmap"> <select id="getHospital" parameterType="hashmap" resultType="hashmap">
/** KioskSql.getHospital **/ /** KioskSql.getHospital **/
SELECT MH.MU_HOSPITAL_ID AS "muHospitalId" SELECT MH.MU_HOSPITAL_ID AS "muHospitalId"

View File

@@ -92,17 +92,17 @@ function fn_selectUser() {
$("#channel").parent().find('button').text(data.userDetail[0].channelName); $("#channel").parent().find('button').text(data.userDetail[0].channelName);
$("#channelCd").val(channel); $("#channelCd").val(channel);
} }
// $("#email").val(data.userDetail[0].email); $("#email").val(data.userDetail[0].email);
$("#userNumber").text(data.userDetail[0].userNumber); $("#userNumber").text(data.userDetail[0].userNumber);
$("#birthday").val(data.userDetail[0].birthday); $("#birthday").val(data.userDetail[0].birthday);
$("#nationality").val(nationality); $("#nationality").val(nationality);
$("#userName").val(data.userDetail[0].userName); $("#userName").val(data.userDetail[0].userName);
// $("#address").val( $("#address").val(
// (data.userDetail && data.userDetail[0] (data.userDetail && data.userDetail[0]
// ? (data.userDetail[0].zipCode ? '(' + data.userDetail[0].zipCode + ') ' : '') + (data.userDetail[0].address || '') ? (data.userDetail[0].zipCode ? '(' + data.userDetail[0].zipCode + ') ' : '') + (data.userDetail[0].address || '')
// : '') : '')
// ); );
// $("#addressDetails").val(data.userDetail[0].addressDetails); $("#addressDetails").val(data.userDetail[0].addressDetails);
$("#identity").text(data.userDetail[0].identity); $("#identity").text(data.userDetail[0].identity);
$("#phoneNumber").val(fn_setFormatPhone(data.userDetail[0].phoneNumber??'')); $("#phoneNumber").val(fn_setFormatPhone(data.userDetail[0].phoneNumber??''));
$("#phoneNumber2").val(fn_setFormatPhone(data.userDetail[0].phoneNumber2??'')); $("#phoneNumber2").val(fn_setFormatPhone(data.userDetail[0].phoneNumber2??''));
@@ -160,7 +160,9 @@ function fn_selectUser() {
document.getElementById("fatPercent").innerText = (userPhysical.fatPercent || '-') + '%'; document.getElementById("fatPercent").innerText = (userPhysical.fatPercent || '-') + '%';
document.getElementById("bmi").innerText = (userPhysical.bmi || '-') + ' kg/m2'; document.getElementById("bmi").innerText = (userPhysical.bmi || '-') + ' kg/m2';
} }
console.log(data);
$("#introName").val(data.userDetail[0].introName);
$("#introName").val(data.userDetail[0].introName);
} else { } else {
modalEvent.danger("조회 오류", data.msgDesc); modalEvent.danger("조회 오류", data.msgDesc);
} }
@@ -236,8 +238,8 @@ function fn_updateUser() {
let etc = $("#etc").val(); let etc = $("#etc").val();
let birthday = $("#birthday").val(); let birthday = $("#birthday").val();
//우편번호 //우편번호
// let address = $("#address").val(); let address = $("#address").val();
// let addressDetails = $("#addressDetails").val().trim(); let addressDetails = $("#addressDetails").val().trim();
// let zipCode = ''; // let zipCode = '';
// let streetAddress= ''; // let streetAddress= '';
// let regex = /\((\d+)\)(.+)/; // let regex = /\((\d+)\)(.+)/;
@@ -251,7 +253,7 @@ function fn_updateUser() {
// let regPassport = /^[A-PR-WYa-pr-wy][1-9]\d\s?\d{4}[1-9]$/; // 대략적인 여권번호 형식 // let regPassport = /^[A-PR-WYa-pr-wy][1-9]\d\s?\d{4}[1-9]$/; // 대략적인 여권번호 형식
let regForeigner = /^[0-9]{6}-[5-8][0-9]{6}$/; // 외국인 등록번호 형식 let regForeigner = /^[0-9]{6}-[5-8][0-9]{6}$/; // 외국인 등록번호 형식
let regPhone = /^[0-9]{8,11}$/; // 전화번호 형식 let regPhone = /^[0-9]{8,11}$/; // 전화번호 형식
// let regEmail = /^([0-9a-zA-Z_\.-]+)@([0-9a-zA-Z_-]+)(\.[0-9a-zA-Z_-]+){1,2}$/; // 이메일 형식 let regEmail = /^([0-9a-zA-Z_\.-]+)@([0-9a-zA-Z_-]+)(\.[0-9a-zA-Z_-]+){1,2}$/; // 이메일 형식
let identification = $("#identification .selected").attr("id"); let identification = $("#identification .selected").attr("id");
let introUserId = document.getElementById('recommendUserId').dataset.userId??''; let introUserId = document.getElementById('recommendUserId').dataset.userId??'';

View File

@@ -1,446 +1,301 @@
<!DOCTYPE html> <!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org" <html lang="ko">
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
<head> <head>
<th:block th:replace="/web/include/head :: layout_head"></th:block> <meta charset="UTF-8">
<th:block layout:fragment="layout_css"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="/css/kiosk/existing-patient.css"> <title>기존 고객 조회 - 메이드유 CRM</title>
</th:block>
<link rel="stylesheet" href="/css/web/font.css">
<link rel="stylesheet" href="/css/kiosk/new-patient.css">
<script src="/js/web/jquery.min.js"></script>
<link rel="stylesheet" href="https://code.jquery.com/ui/1.13.2/themes/base/jquery-ui.css">
<script src="/js/web/jquery-ui.js"></script>
<style>
/* 페이지 기본 레이아웃 */
.page-body.search-mode {
display: flex;
justify-content: center;
align-items: center;
padding-top: 60px;
}
.search-card {
width: 100%;
max-width: 480px;
}
.phone-group {
display: flex;
align-items: center;
gap: 8px;
width: 100%;
}
.phone-group input {
flex: 1;
text-align: center;
height: 44px;
border: 1px solid var(--border-color);
border-radius: 4px;
font-size: 16px;
}
/* [변경] 스크롤 기능을 위한 테이블 컨테이너 */
.user-list-container {
max-height: 450px; /* 약 10row 기준 높이 (1row 당 약 45px) */
overflow-y: auto;
border: 1px solid var(--border-color);
border-radius: 4px;
margin-top: 10px;
}
.user-list-table {
width: 100%;
border-collapse: collapse;
}
/* [변경] 헤더 고정 스타일 */
.user-list-table thead th {
position: sticky;
top: 0;
background-color: #f8f9fa;
padding: 12px 8px;
border-bottom: 2px solid var(--border-color);
font-size: 13px;
color: var(--text-sub);
text-align: center;
z-index: 10;
}
.user-list-table td {
padding: 12px 8px;
border-bottom: 1px solid #eee;
font-size: 14px;
text-align: center;
color: var(--text-main);
cursor: pointer;
}
.user-list-table tr:hover td {
background-color: #f1f8ff;
}
/* 라디오 버튼 스타일 */
.user-radio {
width: 18px;
height: 18px;
accent-color: var(--primary-color);
cursor: pointer;
}
/* jQuery UI 버튼 스타일 */
.btn-registration {
background-color: var(--primary-color) !important;
color: #ffffff !important;
border: 1px solid var(--primary-color) !important;
font-weight: 600 !important;
}
.btn-registration:hover {
background-color: var(--primary-hover) !important;
}
.ui-dialog {
border-radius: 12px !important;
padding: 0 !important;
overflow: hidden;
border: none !important;
box-shadow: 0 10px 30px rgba(0,0,0,0.1) !important;
}
.ui-dialog-titlebar {
background: #fff !important;
border: none !important;
border-bottom: 1px solid var(--border-color) !important;
padding: 15px 20px !important;
}
/* 커스텀 스크롤바 디자인 */
.user-list-container::-webkit-scrollbar {
width: 6px;
}
.user-list-container::-webkit-scrollbar-thumb {
background-color: #ccd3d9;
border-radius: 10px;
}
.user-list-container::-webkit-scrollbar-track {
background-color: #f1f3f5;
}
</style>
</head> </head>
<body>
<div class="new-patient-container">
<header class="page-header">
<h1 class="title">기존 고객 조회</h1>
</header>
<th:block layout:fragment="layout_top_script"> <main class="page-body search-mode">
<script src="/js/kiosk/common.js"></script> <section class="form-section-card search-card">
<script src="/js/web/signature_pad.js"></script> <div class="section-title">
</th:block> 연락처 조회
<span id="phone-error" class="validation-msg" style="visibility: hidden;">번호를 정확히 입력해주세요.</span>
</div>
<th:block layout:fragment="layout_content"> <div class="form-row">
<div id="reserveReadyInsertModal" class="kiosk-container"> <!-- ID kept for JS compatibility --> <label class="required">연락처</label>
<div class="phone-group">
<!-- Header --> <input type="tel" id="phone1" maxlength="3" placeholder="010" inputmode="numeric" value="010">
<header class="page-header"> <span>-</span>
<h1 class="title">기존 고객 바로접수</h1> <input type="tel" id="phone2" maxlength="4" placeholder="0000" inputmode="numeric" value="3873">
</header> <span>-</span>
<input type="tel" id="phone3" maxlength="4" placeholder="0000" inputmode="numeric" value="8265">
<!-- Body -->
<main class="page-body">
<div id="list1" class="form-grid-layout"> <!-- ID kept for JS compatibility -->
<!-- Column 1: Customer Search & Info -->
<section class="form-section-card">
<div class="section-title">고객 조회</div>
<div class="form-row">
<label>고객 검색</label>
<div class="search_box" data-toggle="modal" data-target=".client_inquiry"
style="position:relative; cursor:pointer;">
<input type="hidden" name="modalUserId" />
<input type="text" name="modalUserName" placeholder="터치하여 고객 검색 (이름/연락처)" readonly
style="padding-right: 40px; cursor:pointer;" />
<img src="/image/web/search_B.svg" alt="search"
style="position:absolute; right:12px; top:50%; transform:translateY(-50%); width:18px;">
</div>
</div>
<div class="form-row">
<label>고객 정보</label>
<table class="info-table">
<tr>
<th>고객등급</th>
<td class="modalUserType">-</td>
</tr>
<tr>
<th>생년월일</th>
<td class="modalBirthday">-</td>
</tr>
<tr>
<th>성별</th>
<td class="modalGender">-</td>
</tr>
<tr>
<th>국적</th>
<td class="modalNationality">-</td>
</tr>
<tr>
<th>연락처</th>
<td class="modalPhoneNumber">-</td>
</tr>
<tr>
<th>특이사항</th>
<td class="modalMemo">-</td>
</tr>
</table>
</div>
</section>
<!-- Column 2: Reservation Details -->
<section class="form-section-card reserveRowBox">
<div class="section-title">예약 정보</div>
<!-- Dynamic reservation row will be injected here or statically defined if logic permits.
The original JS appends ".modal_table_content". We will adapt.
For now, we define the structure directly for the FIRST row to avoid JS injection issues on load if possible.
However, legacy JS appends it. Let's keep the container relative.
-->
<!-- We'll let JS append the complicated row, but stylize the container -->
</section>
<!-- Column 3: Staff & Memo -->
<section class="form-section-card">
<div class="section-title">담당자 및 메모</div>
<div class="form-row">
<label>담당자 (직원)</label>
<input type="hidden" name="modalMemberId" />
<!-- Read Mode -->
<div class="read_box" style="display:flex; gap:10px;">
<input type="text" name="modalMemberName" placeholder="담당자 없음" disabled>
<button class="manager_btn" style="width:50px; padding:0; background:#f0f2f5;"><img
src="/image/web/menu_6_off.svg" alt="수정" style="width:20px;"></button>
</div>
<!-- Edit Mode -->
<div class="search_box member_box" style="display:none; position:relative; margin-top:5px;">
<input type="text" name="modalMemberName" readonly placeholder="담당자 검색"
style="padding-right: 80px;" />
<img src="/image/web/search_B.svg"
style="position:absolute; right:60px; top:50%; transform:translateY(-50%); width:18px;">
<button class="cancel_btn"
style="position:absolute; right:0; top:0; height:100%; border-radius:0 4px 4px 0;">취소</button>
</div>
</div>
<div class="form-row" style="margin-top:20px;">
<label>접수 메모</label>
<textarea placeholder="접수 시 참고할 메모를 입력하세요 (선택)" style="height:150px;"></textarea>
</div>
</section>
</div>
</main>
<!-- Footer -->
<footer class="page-footer">
<button type="button" class="cancel_btn btnCancle">취소</button>
<div id="list1"> <!-- Scope required for JS selector -->
<button type="button" class="registration_bth btnSave">접수 완료</button>
</div>
</footer>
</div>
</th:block>
<th:block layout:fragment="layout_popup">
<!-- Member Search Modal (Hidden by default) -->
<div id="memberSearchModal" class="modal fade" tabindex="-1" role="dialog" aria-hidden="true" style="display:none;">
<div class="modal-dialog" style="max-width:600px; margin: 100px auto;">
<div class="modal-content">
<div class="modal-header" style="background:#f8f9fa; border-bottom:1px solid #ddd; padding:15px;">
<h5 class="modal-title" style="margin:0; font-weight:700;">직원 검색</h5>
</div>
<div class="modal-body" style="padding:20px;">
<div style="display:flex; gap:10px; margin-bottom:15px;">
<input id="memberSearchKeyword" type="text" class="form-control" placeholder="이름 또는 연락처"
style="height:40px; flex:1;">
<button id="searchMemberBtn" class="btn btn-primary"
style="height:40px; width:80px; background:#1B66C9; color:#fff; border:none; border-radius:4px;">검색</button>
</div>
<div class="list1_modal_table" style="max-height:300px; overflow-y:auto;">
<table class="table table-hover" style="width:100%; font-size:14px;">
<thead style="background:#f1f3f5;">
<tr>
<th style="padding:10px;">이름</th>
<th style="padding:10px;">연락처</th>
<th style="padding:10px;">부서</th>
</tr>
</thead>
<tbody>
<!-- Dynamic Content -->
</tbody>
</table>
</div>
</div>
<div class="modal-footer" style="padding:15px; border-top:1px solid #ddd;">
<button type="button" class="btn btn-secondary btnCancle" data-dismiss="modal">닫기</button>
</div>
</div>
</div>
</div>
</th:block>
<th:block layout:fragment="layout_script">
<script type="text/javascript">
$(document).ready(function () {
// Page Init
reserveReadyInsertModal.init();
});
let reserveReadyInsertModal = {
callback: null,
reqParam: null,
init: function () {
// Remove legacy modal template injection
// $('#reserveReadyInsertModal').remove();
// $('body').append( this.HtmlTemplate);
// Initialize Events
this.setEvent();
// Add default row
this.addRow();
},
setEvent: function () {
$('.btnCancle').on("click", function () {
history.back();
});
$('#list1 .btnSave').on("click", function () {
reserveReadyInsertModal.save("I");
});
// User Search
$('#list1 input[name="modalUserName"]').on("click", function () {
userIntroSelectModal.popup(function (obj) {
// Populate User Info
$('#list1 input[name="modalUserId"]').val(obj.muuserid);
$('#list1 input[name="modalUserName"]').val(obj.username);
$('#list1 .modalPhoneNumber').text(fn_setFormatPhone(obj.phonenumber));
if ("T" == obj.userType2) {
$('.modalUserType').text('간편예약');
$('.modalBirthday').text(obj.birthday || '-');
} else {
$('.modalUserType').html(obj.usertype ? `<img src="\${obj.usertype.replace('C:', '')}" height="20"/>` : '일반');
$('.modalBirthday').text(obj.birthday);
$('.modalGender').text(obj.gender);
$('.modalNationality').text(obj.nationality || 'KR');
$('.modalMemo').text(obj.memo);
}
});
});
// Manager Edit Toggle
$('#list1 .manager_btn').on("click", function () {
$('#list1 .read_box').hide();
$('#list1 .member_box').show();
});
$('#list1 .cancel_btn').on("click", function () {
$('#list1 .read_box').show();
$('#list1 .member_box').hide();
});
// Manager Search
$('input[name="modalMemberName"]').on("click", function () {
memberSearchModal.popup(function (obj) {
$('input[name="modalMemberName"]').val(obj.selectMemberName);
$('input[name="modalMemberId"]').val(obj.selectMemberId);
// Return to read mode
$('#list1 .read_box').show();
$('#list1 .member_box').hide();
});
});
},
/* Dynamic Row Addition (Re-styled) */
addRow: function () {
let h = `
<div class="form-row modal_table_content" style="border:1px solid #eee; padding:15px; border-radius:8px; margin-bottom:10px;">
<!-- Date -->
<div style="margin-bottom:10px;">
<label>예약일시</label>
<div style="display:flex; gap:10px;">
<div style="flex:1; position:relative;">
<input type="text" class="date_picker" name="modalReserveDate" placeholder="YYYY-MM-DD">
</div>
<div class="select_box dropdown" style="flex:1;">
<button class="label" data-toggle="dropdown">00:00</button>
<input type="hidden" name="modalReserveTime">
<ul class="select_option_list dropdown-menu time"></ul>
</div>
</div>
</div> </div>
</div>
<!-- Category -->
<div style="margin-bottom:10px;">
<label>예약경로</label>
<div class="select_box dropdown">
<button class="label" data-toggle="dropdown">선택하세요</button>
<input type="hidden" name="modalReserveCategoryItemCode">
<ul class="select_option_list dropdown-menu category-list"></ul>
</div>
</div>
<!-- Procedure -->
<div style="margin-bottom:10px;">
<label>진료유형</label>
<div class="search_box">
<input type="hidden" name="modalTreatmentProcedureId" />
<input type="text" name="modalTreatmentProcedureName" readonly placeholder="진료/시술 검색" />
</div>
</div>
<!-- Doctor -->
<div>
<label>닥터/상담</label>
<div class="select_box dropdown">
<button class="label" data-toggle="dropdown">선택하세요</button>
<input type="hidden" name="modalReserveMemberId">
<ul class="select_option_list dropdown-menu doctor-list"></ul>
</div>
</div>
</div>
`;
$('.reserveRowBox').append(h); <div style="margin-top: 20px;">
this.setReserveCategoryCombo(); <button type="button" class="registration_bth" style="width: 100%;" onclick="validateAndSearch()">조회하기</button>
</div>
</section>
</main>
let lastRow = $('.reserveRowBox .modal_table_content').last(); <footer class="page-footer">
<button type="button" class="cancel_btn" onclick="moveKiosk()">뒤로가기</button>
</footer>
</div>
// Init Datepicker <div id="customerDialog" title="고객 선택" style="display:none;">
lastRow.find('.date_picker').datetimepicker({ <p style="margin: 15px 0 10px; font-size: 14px; color: #666;">접수하실 고객님을 선택해 주세요.</p>
format: 'YYYY-MM-DD',
defaultDate: moment() <div class="user-list-container">
}).on("dp.change", function (e) { <table class="user-list-table">
fn_searchSetTime(lastRow.find('.time'), e.date.format("YYYY-MM-DD")); <thead>
}); <tr>
<th>선택</th>
<th>성함</th>
<th>생년월일</th>
<th>성별</th>
<th>최근 방문일</th>
</tr>
</thead>
<tbody id="userListBody">
</tbody>
</table>
</div>
</div>
// Init Time <script>
fn_searchSetTime(lastRow.find('.time'), moment().format("YYYY-MM-DD")); const inputs = [
document.getElementById('phone1'),
document.getElementById('phone2'),
document.getElementById('phone3')
];
// Procedure Search $(document).ready(function() {
lastRow.find('input[name="modalTreatmentProcedureName"]').on("click", function () { // jQuery UI Dialog 설정
treatmentProcedureSearchModal.popup(function (obj) { $("#customerDialog").dialog({
lastRow.find('input[name="modalTreatmentProcedureId"]').val(obj.mutreatmentprocedureid); autoOpen: false,
lastRow.find('input[name="modalTreatmentProcedureName"]').val(obj.treatmentprocedurename); modal: true,
width: 600, // 스크롤바 공간을 고려하여 너비 소폭 확장
resizable: false,
show: { effect: "fade", duration: 200 },
hide: { effect: "fade", duration: 200 },
buttons: [
{
text: "접수하기",
class: "btn-registration",
click: function() {
submitBtn();
}
},
{
text: "닫기",
click: function() {
$(this).dialog("close");
}
}
]
});
// Set Doctor Combo // 행 클릭 시 라디오 버튼 선택
let comboData = fn_selectListReserveMemberOption(lastRow.find('.date_picker').val(), obj.mutreatmentprocedureid); $(document).on('click', '.user-list-table tbody tr', function() {
let html = ''; $(this).find('input[type="radio"]').prop('checked', true);
comboData.forEach(d => { });
html += `<li class="option_list_item" id="doc_\${d.mumemberid}">\${d.typename} \${d.membername}</li>`; });
});
lastRow.find('.doctor-list').html(html);
});
});
// Dropdown Click inputs.forEach((input, index) => {
lastRow.on('click', '.option_list_item', function () { input.addEventListener('input', (e) => {
let txt = $(this).text(); e.target.value = e.target.value.replace(/[^0-9]/g, '');
let id = $(this).attr('id'); if (e.target.value.length === e.target.maxLength && index < 2) {
// Handle id prefix if any (e.g., doc_123) inputs[index + 1].focus();
if (id && id.indexOf('_') > -1) id = id.split('_')[1]; }
});
});
$(this).closest('.dropdown').find('.label').text(txt); function validateAndSearch() {
$(this).closest('.dropdown').find('input[type="hidden"]').val(id); const values = inputs.map(input => input.value);
}); const phoneNumber = values.join('');
},
if (values[1].length < 3 || values[2].length < 4) {
$("#phone-error").css("visibility", "visible");
return;
}
setReserveCategoryCombo: function () { $.ajax({
let category = fn_selectCategoryList(); url: '/kiosk/getUserByPhoneNumber.do',
let reservationCategory = category.find(cat => cat.categoryname === '예약경로'); type: 'POST',
if (reservationCategory) { contentType: 'application/json',
fn_selectModalOtherOption(reservationCategory.categorycode, ''); data: JSON.stringify({ phoneNumber: phoneNumber }),
// The common function targets generic selectors. We might need to manually populate if it fails. success: function(res) {
// Assuming fn_selectModalOtherOption works by finding selectors globally or passed context. let html = "";
// For safety, let's manually populate the last row's category list if possible, if( res.msgCode == 0 ){
// or rely on common.js if it finds '.category-list'. const userList = res.list;
// Common.js usually finds by class. Let's assume it works or we use a custom fetch here if needed.
} if (!userList || userList.length === 0) {
}, alert("조회된 정보가 없습니다.");
return;
}
save: function (state) { userList.forEach((user, index) => {
// Validation & Save Logic (Copied from original but simplified selectors) html += `
let muUserId = $('#list1 input[name="modalUserId"]').val(); <tr>
let userName = $('#list1 input[name="modalUserName"]').val(); <td><input type="radio" name="selectedUser" class="user-radio" value="${user.userNumber}" ${index === 0 ? 'checked' : ''}></td>
<td class="user-name-cell">${user.userName}</td>
<td>${user.birthday || '-'}</td>
<td>${user.gender === 'M' ? '남' : (user.gender === 'F' ? '여' : '-')}</td>
<td>${user.lastVisitDate || '-'}</td>
</tr>
`;
});
} else {
alert(res.msgDesc);
return;
}
// Get reservation details from the generated row $("#userListBody").html(html);
let row = $('.reserveRowBox .modal_table_content').last(); $("#customerDialog").dialog("open");
let reserveDate = row.find('input[name="modalReserveDate"]').val(); },
let reserveTime = row.find('input[name="modalReserveTime"]').val(); error: function() {
let catCode = row.find('input[name="modalReserveCategoryItemCode"]').val(); alert("통신 오류가 발생했습니다.");
let procId = row.find('input[name="modalTreatmentProcedureId"]').val(); }
let docId = row.find('input[name="modalReserveMemberId"]').val(); });
}
let memId = $('input[name="modalMemberId"]').val(); // Staff function submitBtn() {
const selectedUser = $("input[name='selectedUser']:checked");
if (!muUserId) { modalEvent.warning("접수", "고객을 선택해주세요."); return; } if (selectedUser.length === 0) {
if (!reserveDate || !reserveTime) { modalEvent.warning("접수", "예약 일시를 선택해주세요."); return; } alert("접수할 고객을 선택해 주세요.");
if (!procId) { modalEvent.warning("접수", "진료 유형을 선택해주세요."); return; } return;
}
modalEvent.info("접수", "접수를 진행하시겠습니까?", function () { const userName = selectedUser.val();
let formData = new FormData(); alert(userName + "님 접수가 완료되었습니다.");
formData.append("muUserId", muUserId); $("#customerDialog").dialog("close");
formData.append("userName", userName); }
formData.append("reserveDate", reserveDate);
formData.append("reserveTime", reserveTime);
formData.append("reserveCategoryItemCode", catCode);
formData.append("muTreatmentProcedureId", procId);
formData.append("reserveMemberId", docId);
formData.append("muMemberId", memId);
$.ajax({
url: '/webreserve/insertReserveReady.do',
type: 'POST',
data: formData,
processData: false, contentType: false,
success: function (data) {
if (data.msgCode == '0') {
modalEvent.success("성공", "접수가 완료되었습니다.", function () {
history.back();
});
} else {
modalEvent.danger("실패", data.msgDesc);
}
}
});
});
}
};
let memberSearchModal = {
callback: null,
popup: function (cb) {
this.callback = cb;
$('#memberSearchModal').modal('show');
},
setSelect: function (id, name, phone) {
if (this.callback) this.callback({ selectMemberId: id, selectMemberName: name });
$('#memberSearchModal').modal('hide');
}
};
// Search Button Event
$('#searchMemberBtn').on('click', function () {
let kw = $('#memberSearchKeyword').val();
let fd = new FormData();
fd.append("memberSearchKeywordParam", kw);
$.ajax({
url: '/kiosk/getMemberList.do',
type: 'POST',
data: fd, processData: false, contentType: false,
success: function (data) {
if (data.msgCode == '0') {
let tbody = $('#memberSearchModal tbody');
tbody.empty();
data.rows.forEach(m => {
let tr = $(`
<tr style="cursor:pointer;">
<td>\${m.membername}</td>
<td>\${fn_setFormatPhone(m.phonenumber)}</td>
<td>\${m.dutyname}</td>
</tr>
`);
tr.click(() => memberSearchModal.setSelect(m.mumemberid, m.membername, m.phonenumber));
tbody.append(tr);
});
}
}
});
});
</script>
</th:block>
function moveKiosk() {
window.location.href = "/kiosk";
}
</script>
</body>
</html> </html>

View File

@@ -119,7 +119,7 @@
<label class="sub_label">보조 연락처</label> <label class="sub_label">보조 연락처</label>
<input id="phoneNumber2" name="phoneNumber" type="tel" placeholder=""> <input id="phoneNumber2" name="phoneNumber" type="tel" placeholder="">
</div> </div>
<!--<div class="input_box"> <div class="input_box">
<label>이메일</label> <label>이메일</label>
<input id="email" type="text"> <input id="email" type="text">
</div> </div>
@@ -128,7 +128,7 @@
<input class="address" id="address" type="text" readonly="readonly"> <input class="address" id="address" type="text" readonly="readonly">
<button class="address_btn" id="searchAddr" type="button">주소 찾기</button> <button class="address_btn" id="searchAddr" type="button">주소 찾기</button>
<input class="address_detail" id="addressDetails" type="text"> <input class="address_detail" id="addressDetails" type="text">
</div>--> </div>
<div class="input_box"> <div class="input_box">
<label>사진 거부</label> <label>사진 거부</label>
<div class="gender"> <div class="gender">
@@ -177,6 +177,10 @@
<ul class="select_option_list dropdown-menu" id="treatment"> <ul class="select_option_list dropdown-menu" id="treatment">
</ul> </ul>
</div> </div>
<div class="last">
<label for="introName">고객 추천인</label>
<input type="text" id="introName" name="introName" readonly />
</div>
</div> </div>
<div class="input_box two"> <div class="input_box two">
<div> <div>