2026-01-25 작업
This commit is contained in:
@@ -30,6 +30,10 @@ public class DatabaseConfig {
|
||||
factoryBean.setDataSource(dataSource);
|
||||
factoryBean.setMapperLocations(applicationContext.getResources("classpath:/mappers/**/*.xml"));
|
||||
|
||||
org.apache.ibatis.session.Configuration mybatisConfig = new org.apache.ibatis.session.Configuration();
|
||||
mybatisConfig.setMapUnderscoreToCamelCase(true); // 언더바를 카멜케이스로 매핑
|
||||
factoryBean.setConfiguration(mybatisConfig);
|
||||
|
||||
return factoryBean.getObject();
|
||||
}
|
||||
|
||||
|
||||
@@ -37,6 +37,8 @@ public interface Constants {
|
||||
static final public String END_DATE_NONE = "408"; // 서비스 종료일 정보가 없습니다.
|
||||
static final public String USER_SAME = "409"; // 이미 등록되어 있는 사용자입니다.
|
||||
static final public String EBS_ID_UNEQUAL = "410"; // 사용자 정보가 없습니다.
|
||||
|
||||
static final public String SERVER_ERROR = "서버 오류가 발생했습니다."; //서버 오류가 발생했습니다.
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.madeu.crm.kiosk.ctrl;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
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.crm.kiosk.dto.ConsentFormDTO;
|
||||
import com.madeu.crm.kiosk.dto.KioskDTO;
|
||||
import com.madeu.crm.kiosk.dto.UserDTO;
|
||||
import com.madeu.crm.kiosk.service.KioskService;
|
||||
import com.madeu.init.ManagerDraftAction;
|
||||
@@ -224,7 +226,7 @@ public class KioskController extends ManagerDraftAction {
|
||||
} catch (Exception e) {
|
||||
log.error("Error inserting user", e);
|
||||
resultMap.put("msgCode", Constants.FAIL);
|
||||
resultMap.put("msgDesc", "서버 오류가 발생했습니다.");
|
||||
resultMap.put("msgDesc", Constants.SERVER_ERROR);
|
||||
errorMsg.append(e.getMessage());
|
||||
} finally {
|
||||
// 결과 처리 및 로그 기록 (기존과 동일)
|
||||
@@ -234,6 +236,26 @@ public class KioskController extends ManagerDraftAction {
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 고객 정보 조회
|
||||
*/
|
||||
|
||||
19
src/main/java/com/madeu/crm/kiosk/dto/KioskDTO.java
Normal file
19
src/main/java/com/madeu/crm/kiosk/dto/KioskDTO.java
Normal 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;
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import java.util.Map;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
import com.madeu.crm.kiosk.dto.ConsentFormDTO;
|
||||
import com.madeu.crm.kiosk.dto.KioskDTO;
|
||||
import com.madeu.crm.kiosk.dto.UserDTO;
|
||||
|
||||
@Mapper
|
||||
@@ -47,4 +48,6 @@ public interface KioskMAP {
|
||||
List<Map<String, Object>> getReserveUserOptionList(Map<String, Object> paramMap);
|
||||
|
||||
int insertUser(UserDTO dto);
|
||||
|
||||
List<KioskDTO> selectUserByPhoneNumber(KioskDTO dto);
|
||||
}
|
||||
@@ -12,6 +12,7 @@ import org.springframework.stereotype.Service;
|
||||
|
||||
import com.madeu.constants.Constants;
|
||||
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.map.KioskMAP;
|
||||
import com.madeu.dao.web.webuser.WebUserSqlMapDAO;
|
||||
@@ -312,4 +313,28 @@ public class KioskService {
|
||||
public ConsentFormDTO getConsentForm(ConsentFormDTO 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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -16,11 +16,6 @@ spring:
|
||||
aop:
|
||||
proxy-target-class: false
|
||||
|
||||
mybatis:
|
||||
mapper-locations: mybatis/mappers/*.xml
|
||||
configuration:
|
||||
map-underscore-to-camel-case: true
|
||||
|
||||
server:
|
||||
encoding:
|
||||
charset: UTF-8
|
||||
|
||||
@@ -438,6 +438,7 @@
|
||||
END AS "appUseYn"
|
||||
,DATE_FORMAT(MU.APP_INTRO_DATE, '%Y-%m-%d') AS "appIntroDate"
|
||||
,DATE_FORMAT(MU.LEAVE_DATE, '%Y-%m-%d') AS "leaveDate"
|
||||
,INTRO_NAME as "introName"
|
||||
FROM MU_USER AS MU
|
||||
LEFT JOIN MU_CATEGORY_ITEM AS MCIN
|
||||
ON MCIN.MU_CATEGORY_ITEM_ID = MU.NATIONALITY
|
||||
|
||||
@@ -364,6 +364,27 @@
|
||||
AND MU.MU_USER_ID = #{muUserId}
|
||||
</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">
|
||||
/** KioskSql.getHospital **/
|
||||
SELECT MH.MU_HOSPITAL_ID AS "muHospitalId"
|
||||
|
||||
@@ -92,17 +92,17 @@ function fn_selectUser() {
|
||||
$("#channel").parent().find('button').text(data.userDetail[0].channelName);
|
||||
$("#channelCd").val(channel);
|
||||
}
|
||||
// $("#email").val(data.userDetail[0].email);
|
||||
$("#email").val(data.userDetail[0].email);
|
||||
$("#userNumber").text(data.userDetail[0].userNumber);
|
||||
$("#birthday").val(data.userDetail[0].birthday);
|
||||
$("#nationality").val(nationality);
|
||||
$("#userName").val(data.userDetail[0].userName);
|
||||
// $("#address").val(
|
||||
// (data.userDetail && data.userDetail[0]
|
||||
// ? (data.userDetail[0].zipCode ? '(' + data.userDetail[0].zipCode + ') ' : '') + (data.userDetail[0].address || '')
|
||||
// : '')
|
||||
// );
|
||||
// $("#addressDetails").val(data.userDetail[0].addressDetails);
|
||||
$("#address").val(
|
||||
(data.userDetail && data.userDetail[0]
|
||||
? (data.userDetail[0].zipCode ? '(' + data.userDetail[0].zipCode + ') ' : '') + (data.userDetail[0].address || '')
|
||||
: '')
|
||||
);
|
||||
$("#addressDetails").val(data.userDetail[0].addressDetails);
|
||||
$("#identity").text(data.userDetail[0].identity);
|
||||
$("#phoneNumber").val(fn_setFormatPhone(data.userDetail[0].phoneNumber??''));
|
||||
$("#phoneNumber2").val(fn_setFormatPhone(data.userDetail[0].phoneNumber2??''));
|
||||
@@ -160,7 +160,9 @@ function fn_selectUser() {
|
||||
document.getElementById("fatPercent").innerText = (userPhysical.fatPercent || '-') + '%';
|
||||
document.getElementById("bmi").innerText = (userPhysical.bmi || '-') + ' kg/m2';
|
||||
}
|
||||
|
||||
console.log(data);
|
||||
$("#introName").val(data.userDetail[0].introName);
|
||||
$("#introName").val(data.userDetail[0].introName);
|
||||
} else {
|
||||
modalEvent.danger("조회 오류", data.msgDesc);
|
||||
}
|
||||
@@ -236,8 +238,8 @@ function fn_updateUser() {
|
||||
let etc = $("#etc").val();
|
||||
let birthday = $("#birthday").val();
|
||||
//우편번호
|
||||
// let address = $("#address").val();
|
||||
// let addressDetails = $("#addressDetails").val().trim();
|
||||
let address = $("#address").val();
|
||||
let addressDetails = $("#addressDetails").val().trim();
|
||||
// let zipCode = '';
|
||||
// let streetAddress= '';
|
||||
// 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 regForeigner = /^[0-9]{6}-[5-8][0-9]{6}$/; // 외국인 등록번호 형식
|
||||
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 introUserId = document.getElementById('recommendUserId').dataset.userId??'';
|
||||
|
||||
|
||||
@@ -1,446 +1,301 @@
|
||||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"
|
||||
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
|
||||
|
||||
<html lang="ko">
|
||||
<head>
|
||||
<th:block th:replace="/web/include/head :: layout_head"></th:block>
|
||||
<th:block layout:fragment="layout_css">
|
||||
<link rel="stylesheet" href="/css/kiosk/existing-patient.css">
|
||||
</th:block>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>기존 고객 조회 - 메이드유 CRM</title>
|
||||
|
||||
<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>
|
||||
|
||||
<th:block layout:fragment="layout_top_script">
|
||||
<script src="/js/kiosk/common.js"></script>
|
||||
<script src="/js/web/signature_pad.js"></script>
|
||||
</th:block>
|
||||
|
||||
<th:block layout:fragment="layout_content">
|
||||
<div id="reserveReadyInsertModal" class="kiosk-container"> <!-- ID kept for JS compatibility -->
|
||||
|
||||
<!-- Header -->
|
||||
<body>
|
||||
<div class="new-patient-container">
|
||||
<header class="page-header">
|
||||
<h1 class="title">기존 고객 바로접수</h1>
|
||||
<h1 class="title">기존 고객 조회</h1>
|
||||
</header>
|
||||
|
||||
<!-- 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>
|
||||
<main class="page-body search-mode">
|
||||
<section class="form-section-card search-card">
|
||||
<div class="section-title">
|
||||
연락처 조회
|
||||
<span id="phone-error" class="validation-msg" style="visibility: hidden;">번호를 정확히 입력해주세요.</span>
|
||||
</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>
|
||||
<label class="required">연락처</label>
|
||||
<div class="phone-group">
|
||||
<input type="tel" id="phone1" maxlength="3" placeholder="010" inputmode="numeric" value="010">
|
||||
<span>-</span>
|
||||
<input type="tel" id="phone2" maxlength="4" placeholder="0000" inputmode="numeric" value="3873">
|
||||
<span>-</span>
|
||||
<input type="tel" id="phone3" maxlength="4" placeholder="0000" inputmode="numeric" value="8265">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="margin-top: 20px;">
|
||||
<button type="button" class="registration_bth" style="width: 100%;" onclick="validateAndSearch()">조회하기</button>
|
||||
</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>
|
||||
<button type="button" class="cancel_btn" onclick="moveKiosk()">뒤로가기</button>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</th:block>
|
||||
<div id="customerDialog" title="고객 선택" style="display:none;">
|
||||
<p style="margin: 15px 0 10px; font-size: 14px; color: #666;">접수하실 고객님을 선택해 주세요.</p>
|
||||
|
||||
<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;">
|
||||
<div class="user-list-container">
|
||||
<table class="user-list-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="padding:10px;">이름</th>
|
||||
<th style="padding:10px;">연락처</th>
|
||||
<th style="padding:10px;">부서</th>
|
||||
<th>선택</th>
|
||||
<th>성함</th>
|
||||
<th>생년월일</th>
|
||||
<th>성별</th>
|
||||
<th>최근 방문일</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<!-- Dynamic Content -->
|
||||
<tbody id="userListBody">
|
||||
</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();
|
||||
});
|
||||
<script>
|
||||
const inputs = [
|
||||
document.getElementById('phone1'),
|
||||
document.getElementById('phone2'),
|
||||
document.getElementById('phone3')
|
||||
];
|
||||
|
||||
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();
|
||||
$(document).ready(function() {
|
||||
// jQuery UI Dialog 설정
|
||||
$("#customerDialog").dialog({
|
||||
autoOpen: false,
|
||||
modal: true,
|
||||
width: 600, // 스크롤바 공간을 고려하여 너비 소폭 확장
|
||||
resizable: false,
|
||||
show: { effect: "fade", duration: 200 },
|
||||
hide: { effect: "fade", duration: 200 },
|
||||
buttons: [
|
||||
{
|
||||
text: "접수하기",
|
||||
class: "btn-registration",
|
||||
click: function() {
|
||||
submitBtn();
|
||||
}
|
||||
},
|
||||
setEvent: function () {
|
||||
$('.btnCancle').on("click", function () {
|
||||
history.back();
|
||||
{
|
||||
text: "닫기",
|
||||
click: function() {
|
||||
$(this).dialog("close");
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
$('#list1 .btnSave').on("click", function () {
|
||||
reserveReadyInsertModal.save("I");
|
||||
// 행 클릭 시 라디오 버튼 선택
|
||||
$(document).on('click', '.user-list-table tbody tr', function() {
|
||||
$(this).find('input[type="radio"]').prop('checked', true);
|
||||
});
|
||||
});
|
||||
|
||||
// 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);
|
||||
inputs.forEach((input, index) => {
|
||||
input.addEventListener('input', (e) => {
|
||||
e.target.value = e.target.value.replace(/[^0-9]/g, '');
|
||||
if (e.target.value.length === e.target.maxLength && index < 2) {
|
||||
inputs[index + 1].focus();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// 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();
|
||||
});
|
||||
function validateAndSearch() {
|
||||
const values = inputs.map(input => input.value);
|
||||
const phoneNumber = values.join('');
|
||||
|
||||
// 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>
|
||||
|
||||
<!-- 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);
|
||||
this.setReserveCategoryCombo();
|
||||
|
||||
let lastRow = $('.reserveRowBox .modal_table_content').last();
|
||||
|
||||
// Init Datepicker
|
||||
lastRow.find('.date_picker').datetimepicker({
|
||||
format: 'YYYY-MM-DD',
|
||||
defaultDate: moment()
|
||||
}).on("dp.change", function (e) {
|
||||
fn_searchSetTime(lastRow.find('.time'), e.date.format("YYYY-MM-DD"));
|
||||
});
|
||||
|
||||
// Init Time
|
||||
fn_searchSetTime(lastRow.find('.time'), moment().format("YYYY-MM-DD"));
|
||||
|
||||
// Procedure Search
|
||||
lastRow.find('input[name="modalTreatmentProcedureName"]').on("click", function () {
|
||||
treatmentProcedureSearchModal.popup(function (obj) {
|
||||
lastRow.find('input[name="modalTreatmentProcedureId"]').val(obj.mutreatmentprocedureid);
|
||||
lastRow.find('input[name="modalTreatmentProcedureName"]').val(obj.treatmentprocedurename);
|
||||
|
||||
// Set Doctor Combo
|
||||
let comboData = fn_selectListReserveMemberOption(lastRow.find('.date_picker').val(), obj.mutreatmentprocedureid);
|
||||
let html = '';
|
||||
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
|
||||
lastRow.on('click', '.option_list_item', function () {
|
||||
let txt = $(this).text();
|
||||
let id = $(this).attr('id');
|
||||
// Handle id prefix if any (e.g., doc_123)
|
||||
if (id && id.indexOf('_') > -1) id = id.split('_')[1];
|
||||
|
||||
$(this).closest('.dropdown').find('.label').text(txt);
|
||||
$(this).closest('.dropdown').find('input[type="hidden"]').val(id);
|
||||
});
|
||||
},
|
||||
|
||||
setReserveCategoryCombo: function () {
|
||||
let category = fn_selectCategoryList();
|
||||
let reservationCategory = category.find(cat => cat.categoryname === '예약경로');
|
||||
if (reservationCategory) {
|
||||
fn_selectModalOtherOption(reservationCategory.categorycode, '');
|
||||
// The common function targets generic selectors. We might need to manually populate if it fails.
|
||||
// Assuming fn_selectModalOtherOption works by finding selectors globally or passed context.
|
||||
// For safety, let's manually populate the last row's category list if possible,
|
||||
// or rely on common.js if it finds '.category-list'.
|
||||
// Common.js usually finds by class. Let's assume it works or we use a custom fetch here if needed.
|
||||
if (values[1].length < 3 || values[2].length < 4) {
|
||||
$("#phone-error").css("visibility", "visible");
|
||||
return;
|
||||
}
|
||||
},
|
||||
|
||||
save: function (state) {
|
||||
// Validation & Save Logic (Copied from original but simplified selectors)
|
||||
let muUserId = $('#list1 input[name="modalUserId"]').val();
|
||||
let userName = $('#list1 input[name="modalUserName"]').val();
|
||||
|
||||
// Get reservation details from the generated row
|
||||
let row = $('.reserveRowBox .modal_table_content').last();
|
||||
let reserveDate = row.find('input[name="modalReserveDate"]').val();
|
||||
let reserveTime = row.find('input[name="modalReserveTime"]').val();
|
||||
let catCode = row.find('input[name="modalReserveCategoryItemCode"]').val();
|
||||
let procId = row.find('input[name="modalTreatmentProcedureId"]').val();
|
||||
let docId = row.find('input[name="modalReserveMemberId"]').val();
|
||||
|
||||
let memId = $('input[name="modalMemberId"]').val(); // Staff
|
||||
|
||||
if (!muUserId) { modalEvent.warning("접수", "고객을 선택해주세요."); return; }
|
||||
if (!reserveDate || !reserveTime) { modalEvent.warning("접수", "예약 일시를 선택해주세요."); return; }
|
||||
if (!procId) { modalEvent.warning("접수", "진료 유형을 선택해주세요."); return; }
|
||||
|
||||
modalEvent.info("접수", "접수를 진행하시겠습니까?", function () {
|
||||
let formData = new FormData();
|
||||
formData.append("muUserId", muUserId);
|
||||
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',
|
||||
url: '/kiosk/getUserByPhoneNumber.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);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
contentType: 'application/json',
|
||||
data: JSON.stringify({ phoneNumber: phoneNumber }),
|
||||
success: function(res) {
|
||||
let html = "";
|
||||
if( res.msgCode == 0 ){
|
||||
const userList = res.list;
|
||||
|
||||
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');
|
||||
if (!userList || userList.length === 0) {
|
||||
alert("조회된 정보가 없습니다.");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
// 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>
|
||||
userList.forEach((user, index) => {
|
||||
html += `
|
||||
<tr>
|
||||
<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>
|
||||
`);
|
||||
tr.click(() => memberSearchModal.setSelect(m.mumemberid, m.membername, m.phonenumber));
|
||||
tbody.append(tr);
|
||||
`;
|
||||
});
|
||||
} else {
|
||||
alert(res.msgDesc);
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$("#userListBody").html(html);
|
||||
$("#customerDialog").dialog("open");
|
||||
},
|
||||
error: function() {
|
||||
alert("통신 오류가 발생했습니다.");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function submitBtn() {
|
||||
const selectedUser = $("input[name='selectedUser']:checked");
|
||||
if (selectedUser.length === 0) {
|
||||
alert("접수할 고객을 선택해 주세요.");
|
||||
return;
|
||||
}
|
||||
const userName = selectedUser.val();
|
||||
alert(userName + "님 접수가 완료되었습니다.");
|
||||
$("#customerDialog").dialog("close");
|
||||
}
|
||||
|
||||
function moveKiosk() {
|
||||
window.location.href = "/kiosk";
|
||||
}
|
||||
</script>
|
||||
</th:block>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -119,7 +119,7 @@
|
||||
<label class="sub_label">보조 연락처</label>
|
||||
<input id="phoneNumber2" name="phoneNumber" type="tel" placeholder="">
|
||||
</div>
|
||||
<!--<div class="input_box">
|
||||
<div class="input_box">
|
||||
<label>이메일</label>
|
||||
<input id="email" type="text">
|
||||
</div>
|
||||
@@ -128,7 +128,7 @@
|
||||
<input class="address" id="address" type="text" readonly="readonly">
|
||||
<button class="address_btn" id="searchAddr" type="button">주소 찾기</button>
|
||||
<input class="address_detail" id="addressDetails" type="text">
|
||||
</div>-->
|
||||
</div>
|
||||
<div class="input_box">
|
||||
<label>사진 거부</label>
|
||||
<div class="gender">
|
||||
@@ -177,6 +177,10 @@
|
||||
<ul class="select_option_list dropdown-menu" id="treatment">
|
||||
</ul>
|
||||
</div>
|
||||
<div class="last">
|
||||
<label for="introName">고객 추천인</label>
|
||||
<input type="text" id="introName" name="introName" readonly />
|
||||
</div>
|
||||
</div>
|
||||
<div class="input_box two">
|
||||
<div>
|
||||
|
||||
Reference in New Issue
Block a user