Compare commits
34 Commits
6ff468b5e0
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 441f3ce30b | |||
| 8e26533e2f | |||
| cd7f38378d | |||
| 6ee8124cda | |||
| 06c2a8c90e | |||
| 9b6d4dc4c8 | |||
| 4d74b497d4 | |||
| f3d3780ef9 | |||
| cd0fb5584e | |||
| ce39cc5c99 | |||
| f7e00b745f | |||
| 259af715b8 | |||
| f0a61d80d9 | |||
| b4248b7bf3 | |||
| fbe06f35dd | |||
| 559e99b050 | |||
| cacfdfb943 | |||
| bec0b3338b | |||
|
|
957652682f | ||
|
|
75618dad0d | ||
|
|
4f2b9fd2a2 | ||
|
|
8b637c0a84 | ||
|
|
ac1ca3020a | ||
|
|
81ff7701b6 | ||
|
|
38fa238bc5 | ||
|
|
3e0cdab291 | ||
|
|
f935efaa93 | ||
|
|
9efdac26cb | ||
|
|
6bb3ae8f7c | ||
|
|
b73b3fd3f7 | ||
|
|
37664d94ef | ||
|
|
677bd4d818 | ||
|
|
76bbfca086 | ||
|
|
c5d4bee27c |
1
.wiki
Submodule
11
Walkthrough.md
Normal file
@@ -0,0 +1,11 @@
|
||||
# MadeU Diet Home - 프로젝트 Walkthrough
|
||||
|
||||
최근 개발 환경 및 시스템에 적용된 주요 변경 사항과 기능 구현 내역을 요약한 문서입니다.
|
||||
|
||||
## 1. 프로젝트 초기 구성 및 구동 안정화
|
||||
- **저장소 구축**: Git 저장소 초기 클론을 통한 환경 구성 및 초기화를 완료했습니다.
|
||||
- **프록시 및 로컬 구동 트러블슈팅**: Jenkins를 넘나드는 리버스 프록시(Reverse Proxy) 설정 오류 및 로컬 개발 환경에서 프로젝트 구동 시 발생했던 구성 요소 이슈를 파악하고 정상화했습니다.
|
||||
|
||||
## 2. 검색 엔진 최적화(SEO) 및 메타 데이터 구축
|
||||
- **메타 태그 수정**: 웹 페이지 헤더 영역의 `meta` 태그 관련 속성을 최적화하여 페이지 렌더링 및 정보 제공의 정확도를 향상시켰습니다.
|
||||
- **Open Graph (OG) 태그 적용**: 카카오톡, 페이스북, 인스타그램 등 외부 소셜 미디어를 통해 웹사이트 링크가 공유될 때, 알맞은 이미지와 설명 및 타이틀이 카드 형태로 매끄럽게 표시되도록 `og:title`, `og:image`, `og:description` 등의 기능을 추가했습니다.
|
||||
94
rules.md
Normal file
@@ -0,0 +1,94 @@
|
||||
# 프로젝트 코딩 가이드라인 (Java Backend)
|
||||
|
||||
AI 에디터(Agent)는 다음 규칙을 항상 준수하여 코드를 작성하고 수정해야 합니다.
|
||||
|
||||
## 0. 기본 소통 규칙 (Communication)
|
||||
- **언어**: 사용자에 대한 모든 답변과 코드 설명은 항상 **한글(Korean)**로만 작성해야 합니다.
|
||||
|
||||
## 1. 패키지 구성 (Package Structure)
|
||||
- **베이스 패키지**: `com.madeuhome`
|
||||
- **컨트롤러 (Controller)**: `ctrl`
|
||||
- **서비스 (Service)**: `svc`
|
||||
- **DTO (Data Transfer Object)**: `dto`
|
||||
- **매퍼 (Mapper)**: `mapper`
|
||||
- **공통 서비스**: `com.madeuhome.common.service` (LogHistoryService 등 시스템 공통 모듈)
|
||||
|
||||
## 2. 파일 명명 규칙 및 구성 (File Naming Conventions)
|
||||
- **컨트롤러 (Controller)**: `[도메인명]Controller.java` (예: `ABCDController.java`)
|
||||
- **서비스 (Service)**: `[도메인명]Service.java` (인터페이스와 구현체(impl)를 분리하지 않고 Service 클래스 파일 하나로만 구현, 예: `ABCDService.java`)
|
||||
- **DTO**: `[도메인명]DTO.java` (예: `ABCDDTO.java`)
|
||||
- **매퍼 (Mapper)**: `[도메인명]Mapper.java` (예: `ABCDMapper.java`)
|
||||
- **XML Mapper**: `[도메인명]SqlMap.xml` (namespace는 Mapper 인터페이스의 **FQCN**과 반드시 일치)
|
||||
|
||||
## 3. URL 및 메소드 명명 규칙 (RequestMapping & Method Naming)
|
||||
|
||||
### 1) RequestMapping (URL) 및 컨트롤러 메소드명
|
||||
- 페이지 이동하는 url : `moveXXXX.do`
|
||||
- 팝업 오픈하는 url : `openXXXX.do`
|
||||
- 조회 url : `getXXXX.do`
|
||||
- 저장 url : `putXXXX.do`
|
||||
- 수정 url : `modXXXX.do`
|
||||
- 삭제 url : `delXXXX.do`
|
||||
- **단, 컨트롤러 메소드명은 위 url에서 `.do`를 제외한 이름과 동일하게 명명합니다.**
|
||||
|
||||
### 2) 서비스 메소드명
|
||||
- 서비스 메소드명은 **컨트롤러 메소드명과 동일**하게 명명합니다.
|
||||
- 조회: `getXXXX` / 목록 조회: `getXXXXList`
|
||||
- 저장: `putXXXX`
|
||||
- 수정: `modXXXX`
|
||||
- 삭제: `delXXXX`
|
||||
|
||||
### 3) Mapper 인터페이스 메소드명
|
||||
- 단일조회 : `selectXXXX`
|
||||
- 리스트조회 : `selectListXXXX`
|
||||
- insert : `insertXXXX`
|
||||
- update : `updateXXXX`
|
||||
- delete : `deleteXXXX`
|
||||
|
||||
## 4. 데이터베이스 연동 정보 (DB Connection)
|
||||
- `application-local.yml`의 설정을 기반으로 한 공통 접속 정보입니다.
|
||||
- **Host**: 183.98.184.84
|
||||
- **Port**: 3306
|
||||
- **Database**: madeu
|
||||
- **User**: madeu
|
||||
- **Password**: apdlemdb12#$
|
||||
|
||||
## 5. 아키텍처 및 코딩 원칙 (Architecture & Coding Principles)
|
||||
|
||||
### 5-1. 컨트롤러 원칙 (Skinny Controller)
|
||||
- 컨트롤러에는 비즈니스 로직이나 예외처리 로직을 넣지 않고, **서비스 메서드를 호출하는 1줄로만 작성**합니다.
|
||||
- 컨트롤러 클래스는 `@Controller` 대신 **`@RestController`**를 사용하며, `@ResponseBody`는 생략합니다.
|
||||
- 데이터 입출력 메소드의 파라미터는 **단일 DTO 하나만 `@RequestBody`**로 받습니다.
|
||||
- 파일 업로드가 포함된 경우에만 `@ModelAttribute` + `@RequestParam MultipartFile`을 허용합니다.
|
||||
- 화면 이동(`move~`) 메소드는 **`ModelAndView`를 리턴**합니다. (`@RestController`에서 String 리턴 시 뷰 이름이 아닌 응답 바디로 해석되므로)
|
||||
- 화면 이동 메소드의 뷰 경로는 **컨트롤러에서 직접 명시**합니다. (서비스에 위임 금지)
|
||||
|
||||
### 5-2. 서비스 원칙 (Service Layer)
|
||||
- 에러 처리(try-catch), 응답 메시지(msgCode, msgDesc) 설정은 **서비스 계층에서 전담**합니다.
|
||||
- `HttpServletRequest`, `HttpSession`은 `@Autowired`로 직접 주입받아 사용합니다. (Spring이 Request-scope 프록시로 제공)
|
||||
- `session.getAttribute("loginMemberId")`를 통해 로그인 ID를 가져오고, DTO에 설정합니다.
|
||||
|
||||
### 5-3. DTO 원칙 (DTO Communication)
|
||||
- **HashMap 사용 금지**. 컨트롤러 ↔ 서비스 ↔ 매퍼 간 모든 데이터는 **DTO 객체만 사용**합니다.
|
||||
- DTO에는 `@Data` (Lombok)을 사용합니다.
|
||||
- DTO 필드 구성:
|
||||
- **DB 컬럼 매핑 필드**: `muProcedureReviewId`, `title`, `content` 등
|
||||
- **조회 결과 전용 필드**: `rowNum`, `writeDate`, `writeName` 등
|
||||
- **검색/UI 변수**: `startDate`, `endDate`, `start`, `limit`, `sort`, `dir` 등
|
||||
- **응답 매핑 변수**: `msgCode`, `msgDesc`, `success`, `totalCount`, `rows`(Object 타입), `tId`
|
||||
|
||||
### 5-4. Mapper 원칙 (MyBatis Mapper)
|
||||
- Mapper는 **`@Mapper` 어노테이션을 사용한 인터페이스**로 작성합니다. (`SqlSessionDaoSupport` 상속 금지)
|
||||
- XML Mapper의 `namespace`는 Mapper 인터페이스의 **FQCN(Fully Qualified Class Name)**과 일치시킵니다.
|
||||
- XML의 `resultType`은 `hashmap` 대신 **DTO FQCN**을 사용합니다.
|
||||
- 예외: 도메인 외부 테이블 조회(카테고리 등)는 `hashmap` 허용
|
||||
- XML alias는 **DTO 필드명(camelCase)**과 정확히 일치시킵니다.
|
||||
- 단건 조회는 `List` 대신 **DTO 단일 객체를 리턴**합니다.
|
||||
|
||||
## 6. 파일 업로드 규칙 (File Upload)
|
||||
- `MultipartFile.transferTo()` 사용 시 반드시 **절대경로를 명시**합니다:
|
||||
```java
|
||||
File dest = new File(outDir, savedName);
|
||||
file.transferTo(dest.toPath().toAbsolutePath());
|
||||
```
|
||||
- 상대경로 사용 시 Tomcat 임시 디렉토리 기준으로 해석되어 오류가 발생합니다.
|
||||
@@ -1,6 +1,5 @@
|
||||
package com.madeuhome.common.ctrl;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -24,12 +23,13 @@ public class MenuController {
|
||||
String requestURI = request.getRequestURI();
|
||||
|
||||
// 특정 URL 패턴에만 메뉴 추가
|
||||
if (requestURI.endsWith("Intro.do") || requestURI.startsWith("/index") ) {
|
||||
return menuService.getMenuHierarchy("MAIN");
|
||||
}
|
||||
/*
|
||||
* if (requestURI.endsWith("Intro.do") || requestURI.startsWith("/index") ) {
|
||||
* return menuService.getMenuHierarchy("MAIN"); }
|
||||
*/
|
||||
|
||||
|
||||
return new ArrayList<>();
|
||||
return menuService.getMenuHierarchy("MAIN");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,209 @@
|
||||
package com.madeuhome.controller.web.reservation;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
|
||||
import com.madeuhome.constants.Constants;
|
||||
import com.madeuhome.service.web.reservation.ReservationService;
|
||||
import com.madeuhome.util.HttpUtil;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.servlet.http.HttpSession;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* 예약 컨트롤러
|
||||
*/
|
||||
@Controller
|
||||
@Slf4j
|
||||
public class ReservationController {
|
||||
|
||||
@Autowired
|
||||
private ReservationService reservationService;
|
||||
|
||||
/**
|
||||
* 예약 페이지 이동
|
||||
*/
|
||||
@RequestMapping(value = "/webservice/selectMakeReservation.do")
|
||||
public String selectMakeReservation(HttpSession session, HttpServletRequest request, Model model) {
|
||||
log.debug("ReservationController selectMakeReservation START");
|
||||
HashMap<String, Object> paramMap = HttpUtil.getParameterMap(request);
|
||||
model.addAttribute("CATEGORY_DIV_CD", paramMap.get("CATEGORY_DIV_CD"));
|
||||
model.addAttribute("CATEGORY_NO", paramMap.get("CATEGORY_NO"));
|
||||
model.addAttribute("POST_NO", paramMap.get("POST_NO"));
|
||||
model.addAttribute("PROCEDURE_ID", paramMap.get("PROCEDURE_ID"));
|
||||
log.debug("ReservationController selectMakeReservation END");
|
||||
return "/web/service/makeReservation";
|
||||
}
|
||||
|
||||
/**
|
||||
* 예약 시술 정보 조회
|
||||
*/
|
||||
@RequestMapping(value = "/webservice/selectReservation.do")
|
||||
public ModelAndView selectReservation(HttpSession session, HttpServletRequest request,
|
||||
HttpServletResponse response) {
|
||||
log.debug("ReservationController selectReservation START");
|
||||
|
||||
HashMap<String, Object> paramMap = HttpUtil.getParameterMap(request);
|
||||
HashMap<String, Object> map = new HashMap<>();
|
||||
StringBuffer errorMsg = new StringBuffer();
|
||||
|
||||
try {
|
||||
map = reservationService.selectReservation(paramMap);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
errorMsg.append(e);
|
||||
return null;
|
||||
} finally {
|
||||
if (Constants.OK == map.get("msgCode")) {
|
||||
} else {
|
||||
if (null == map.get("msgCode") || ("").equals(map.get("msgCode"))) {
|
||||
map.put("msgCode", Constants.FAIL);
|
||||
}
|
||||
map.put("success", false);
|
||||
if (null == map.get("msgDesc") || ("").equals(map.get("msgDesc"))) {
|
||||
map.put("msgDesc", "정상적으로 수행되지 않았습니다. 관리자에게 문의하시기 바랍니다.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log.debug("ReservationController selectReservation END");
|
||||
return HttpUtil.makeHashToJsonModelAndView(map);
|
||||
}
|
||||
|
||||
/**
|
||||
* 병원 휴일 조회 (달력 표출용)
|
||||
*/
|
||||
@RequestMapping(value = "/webservice/selectHospitalHolidayList.do")
|
||||
public ModelAndView selectHospitalHolidayList(HttpSession session, HttpServletRequest request,
|
||||
HttpServletResponse response) {
|
||||
log.debug("ReservationController selectHospitalHolidayList START");
|
||||
|
||||
HashMap<String, Object> paramMap = HttpUtil.getParameterMap(request);
|
||||
HashMap<String, Object> map = new HashMap<>();
|
||||
|
||||
try {
|
||||
map = reservationService.selectHospitalHolidayList(paramMap);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
} finally {
|
||||
if (Constants.OK != map.get("msgCode")) {
|
||||
map.put("msgCode", Constants.FAIL);
|
||||
map.put("success", false);
|
||||
if (null == map.get("msgDesc") || ("").equals(map.get("msgDesc"))) {
|
||||
map.put("msgDesc", "정상적으로 수행되지 않았습니다. 관리자에게 문의하시기 바랍니다.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log.debug("ReservationController selectHospitalHolidayList END");
|
||||
return HttpUtil.makeHashToJsonModelAndView(map);
|
||||
}
|
||||
|
||||
/**
|
||||
* 특정 날짜 운영시간 조회
|
||||
*/
|
||||
@RequestMapping(value = "/webservice/selectHospitalWorkTime.do")
|
||||
public ModelAndView selectHospitalWorkTime(HttpSession session, HttpServletRequest request,
|
||||
HttpServletResponse response) {
|
||||
log.debug("ReservationController selectHospitalWorkTime START");
|
||||
|
||||
HashMap<String, Object> paramMap = HttpUtil.getParameterMap(request);
|
||||
HashMap<String, Object> map = new HashMap<>();
|
||||
|
||||
try {
|
||||
map = reservationService.selectHospitalWorkTime(paramMap);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
} finally {
|
||||
if (Constants.OK != map.get("msgCode")) {
|
||||
map.put("msgCode", Constants.FAIL);
|
||||
map.put("success", false);
|
||||
if (null == map.get("msgDesc") || ("").equals(map.get("msgDesc"))) {
|
||||
map.put("msgDesc", "정상적으로 수행되지 않았습니다. 관리자에게 문의하시기 바랍니다.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log.debug("ReservationController selectHospitalWorkTime END");
|
||||
return HttpUtil.makeHashToJsonModelAndView(map);
|
||||
}
|
||||
|
||||
/**
|
||||
* 예약 카운트 조회
|
||||
*/
|
||||
@RequestMapping(value = "/webservice/selectReservationCnt.do")
|
||||
public ModelAndView selectReservationCnt(HttpSession session, HttpServletRequest request,
|
||||
HttpServletResponse response) {
|
||||
log.debug("ReservationController selectReservationCnt START");
|
||||
|
||||
HashMap<String, Object> paramMap = HttpUtil.getParameterMap(request);
|
||||
HashMap<String, Object> map = new HashMap<>();
|
||||
StringBuffer errorMsg = new StringBuffer();
|
||||
|
||||
try {
|
||||
map = reservationService.selectReservationCnt(paramMap);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
errorMsg.append(e);
|
||||
return null;
|
||||
} finally {
|
||||
if (Constants.OK == map.get("msgCode")) {
|
||||
} else {
|
||||
if (null == map.get("msgCode") || ("").equals(map.get("msgCode"))) {
|
||||
map.put("msgCode", Constants.FAIL);
|
||||
}
|
||||
map.put("success", false);
|
||||
if (null == map.get("msgDesc") || ("").equals(map.get("msgDesc"))) {
|
||||
map.put("msgDesc", "정상적으로 수행되지 않았습니다. 관리자에게 문의하시기 바랍니다.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log.debug("ReservationController selectReservationCnt END");
|
||||
return HttpUtil.makeHashToJsonModelAndView(map);
|
||||
}
|
||||
|
||||
/**
|
||||
* 예약 저장
|
||||
*/
|
||||
@RequestMapping(value = "/webservice/insertReservation.do")
|
||||
public ModelAndView insertReservation(HttpSession session, HttpServletRequest request,
|
||||
HttpServletResponse response) {
|
||||
log.debug("ReservationController insertReservation START");
|
||||
|
||||
HashMap<String, Object> paramMap = HttpUtil.getParameterMap(request);
|
||||
HashMap<String, Object> map = new HashMap<>();
|
||||
StringBuffer errorMsg = new StringBuffer();
|
||||
|
||||
try {
|
||||
map = reservationService.insertReservation(paramMap);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
errorMsg.append(e);
|
||||
return null;
|
||||
} finally {
|
||||
if (Constants.OK == map.get("msgCode")) {
|
||||
} else {
|
||||
if (null == map.get("msgCode") || ("").equals(map.get("msgCode"))) {
|
||||
map.put("msgCode", Constants.FAIL);
|
||||
}
|
||||
map.put("success", false);
|
||||
if (null == map.get("msgDesc") || ("").equals(map.get("msgDesc"))) {
|
||||
map.put("msgDesc", "정상적으로 수행되지 않았습니다. 관리자에게 문의하시기 바랍니다.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log.debug("ReservationController insertReservation END");
|
||||
return HttpUtil.makeHashToJsonModelAndView(map);
|
||||
}
|
||||
}
|
||||
@@ -18,10 +18,9 @@ import org.springframework.web.servlet.ModelAndView;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
|
||||
@Slf4j
|
||||
@Controller
|
||||
public class WebEventController extends ManagerDraftAction{
|
||||
public class WebEventController extends ManagerDraftAction {
|
||||
|
||||
@Autowired
|
||||
private WebEventService webEventService;
|
||||
@@ -36,8 +35,8 @@ public class WebEventController extends ManagerDraftAction{
|
||||
* @param response
|
||||
* @return
|
||||
*/
|
||||
@RequestMapping(value="/webevent/selectListWebEventIntro.do")
|
||||
public String selectListWebEventIntro(HttpSession session,HttpServletRequest request) {
|
||||
@RequestMapping(value = "/webevent/selectListWebEventIntro.do")
|
||||
public String selectListWebEventIntro(HttpSession session, HttpServletRequest request) {
|
||||
|
||||
log.debug("WebEventController selectListWebEventIntro START");
|
||||
|
||||
@@ -52,33 +51,34 @@ public class WebEventController extends ManagerDraftAction{
|
||||
* @param response
|
||||
* @return
|
||||
*/
|
||||
@RequestMapping(value="/webevent/selectListWebEvent.do")
|
||||
public ModelAndView selectListWebEvent(HttpSession session,HttpServletRequest request, HttpServletResponse response) {
|
||||
@RequestMapping(value = "/webevent/selectListWebEvent.do")
|
||||
public ModelAndView selectListWebEvent(HttpSession session, HttpServletRequest request,
|
||||
HttpServletResponse response) {
|
||||
|
||||
log.debug("WebEventController selectListWebEvent START");
|
||||
|
||||
|
||||
HashMap<String, Object> paramMap = HttpUtil.getParameterMap(request);
|
||||
HashMap<String, Object> map = new HashMap<String, Object>();
|
||||
StringBuffer errorMsg = new StringBuffer();
|
||||
|
||||
try{
|
||||
|
||||
try {
|
||||
map = webEventService.selectListWebEvent(paramMap);
|
||||
log.debug(map + "");
|
||||
}catch (Exception e) {
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
errorMsg.append(e);
|
||||
return null;
|
||||
}finally {
|
||||
if(Constants.OK == map.get("msgCode")) {
|
||||
} finally {
|
||||
if (Constants.OK == map.get("msgCode")) {
|
||||
|
||||
}else{
|
||||
if(null == map.get("msgCode") || ("").equals(map.get("msgCode"))) {
|
||||
} else {
|
||||
if (null == map.get("msgCode") || ("").equals(map.get("msgCode"))) {
|
||||
map.put("msgCode", Constants.FAIL);
|
||||
}
|
||||
|
||||
map.put("success", false);
|
||||
if(null == map.get("msgDesc") || ("").equals(map.get("msgDesc"))) {
|
||||
map.put("msgDesc","정상적으로 수행되지 않았습니다. 관리자에게 문의하시기 바랍니다. (E0029)");
|
||||
if (null == map.get("msgDesc") || ("").equals(map.get("msgDesc"))) {
|
||||
map.put("msgDesc", "정상적으로 수행되지 않았습니다. 관리자에게 문의하시기 바랍니다. (E0029)");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,27 +94,26 @@ public class WebEventController extends ManagerDraftAction{
|
||||
insertMap.put("requestValue", String.valueOf(paramMap));
|
||||
insertMap.put("responseValue", String.valueOf(map));
|
||||
;
|
||||
if(("true").equals(String.valueOf(map.get("success")))){
|
||||
if (("true").equals(String.valueOf(map.get("success")))) {
|
||||
insertMap.put("resultCode", "SUCCESS");
|
||||
}else{
|
||||
} else {
|
||||
insertMap.put("resultCode", "ERROR");
|
||||
}
|
||||
|
||||
insertMap.put("resultMsg", String.valueOf(errorMsg));
|
||||
insertMap.put("muMemberId", paramMap.get("muMemberId"));
|
||||
|
||||
// logHistoryService.insertLogHistory(insertMap, visitLogParamMap);
|
||||
// logHistoryService.insertLogHistory(insertMap, visitLogParamMap);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
log.debug("WebEventController selectListWebEvent END");
|
||||
|
||||
|
||||
|
||||
return HttpUtil.makeHashToJsonModelAndView(map);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 이벤트 별 항목 목록 조회
|
||||
*
|
||||
@@ -122,33 +121,33 @@ public class WebEventController extends ManagerDraftAction{
|
||||
* @param response
|
||||
* @return
|
||||
*/
|
||||
@RequestMapping(value="/webevent/selectListEvent.do")
|
||||
public ModelAndView selectListEvent(HttpSession session,HttpServletRequest request, HttpServletResponse response) {
|
||||
@RequestMapping(value = "/webevent/selectListEvent.do")
|
||||
public ModelAndView selectListEvent(HttpSession session, HttpServletRequest request, HttpServletResponse response) {
|
||||
|
||||
log.debug("WebEventController selectListEvent START");
|
||||
|
||||
|
||||
HashMap<String, Object> paramMap = HttpUtil.getParameterMap(request);
|
||||
HashMap<String, Object> map = new HashMap<String, Object>();
|
||||
StringBuffer errorMsg = new StringBuffer();
|
||||
|
||||
try{
|
||||
|
||||
try {
|
||||
map = webEventService.selectListEvent(paramMap);
|
||||
log.debug(map + "");
|
||||
}catch (Exception e) {
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
errorMsg.append(e);
|
||||
return null;
|
||||
}finally {
|
||||
if(Constants.OK == map.get("msgCode")) {
|
||||
} finally {
|
||||
if (Constants.OK == map.get("msgCode")) {
|
||||
|
||||
}else{
|
||||
if(null == map.get("msgCode") || ("").equals(map.get("msgCode"))) {
|
||||
} else {
|
||||
if (null == map.get("msgCode") || ("").equals(map.get("msgCode"))) {
|
||||
map.put("msgCode", Constants.FAIL);
|
||||
}
|
||||
|
||||
map.put("success", false);
|
||||
if(null == map.get("msgDesc") || ("").equals(map.get("msgDesc"))) {
|
||||
map.put("msgDesc","정상적으로 수행되지 않았습니다. 관리자에게 문의하시기 바랍니다. (E0029)");
|
||||
if (null == map.get("msgDesc") || ("").equals(map.get("msgDesc"))) {
|
||||
map.put("msgDesc", "정상적으로 수행되지 않았습니다. 관리자에게 문의하시기 바랍니다. (E0029)");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -164,23 +163,22 @@ public class WebEventController extends ManagerDraftAction{
|
||||
insertMap.put("requestValue", String.valueOf(paramMap));
|
||||
insertMap.put("responseValue", String.valueOf(map));
|
||||
;
|
||||
if(("true").equals(String.valueOf(map.get("success")))){
|
||||
if (("true").equals(String.valueOf(map.get("success")))) {
|
||||
insertMap.put("resultCode", "SUCCESS");
|
||||
}else{
|
||||
} else {
|
||||
insertMap.put("resultCode", "ERROR");
|
||||
}
|
||||
|
||||
insertMap.put("resultMsg", String.valueOf(errorMsg));
|
||||
|
||||
// logHistoryService.insertLogHistory(insertMap, visitLogParamMap);
|
||||
// logHistoryService.insertLogHistory(insertMap, visitLogParamMap);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
log.debug("WebEventController selectListEvent END");
|
||||
|
||||
|
||||
|
||||
return HttpUtil.makeHashToJsonModelAndView(map);
|
||||
}
|
||||
|
||||
@@ -191,19 +189,19 @@ public class WebEventController extends ManagerDraftAction{
|
||||
* @param response
|
||||
* @return
|
||||
*/
|
||||
@RequestMapping(value="/webevent/selectEventDetailIntro.do")
|
||||
@RequestMapping(value = "/webevent/selectEventDetailIntro.do")
|
||||
public String selectEventDetailIntro(HttpSession session, HttpServletRequest request, Model model) {
|
||||
|
||||
log.debug("WebEventController selectEventDetailIntro START");
|
||||
HashMap<String, Object> paramMap = HttpUtil.getParameterMap(request);
|
||||
model.addAttribute("CATEGORY_DIV_CD", paramMap.get("CATEGORY_DIV_CD"));
|
||||
model.addAttribute("CATEGORY_NO", paramMap.get("CATEGORY_NO"));
|
||||
model.addAttribute("POST_NO", paramMap.get("POST_NO"));
|
||||
model.addAttribute("POST_NO", paramMap.get("POST_NO"));
|
||||
log.debug("WebEventController selectEventDetailIntro END");
|
||||
|
||||
|
||||
return "/web/webevent/webEventSelect";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 이벤트 안내 목록 상세 조회
|
||||
*
|
||||
@@ -211,33 +209,34 @@ public class WebEventController extends ManagerDraftAction{
|
||||
* @param response
|
||||
* @return
|
||||
*/
|
||||
@RequestMapping(value="/webevent/selectEventDetail.do")
|
||||
public ModelAndView selectEventDetail(HttpSession session, HttpServletRequest request, HttpServletResponse response) {
|
||||
@RequestMapping(value = "/webevent/selectEventDetail.do")
|
||||
public ModelAndView selectEventDetail(HttpSession session, HttpServletRequest request,
|
||||
HttpServletResponse response) {
|
||||
|
||||
log.debug("WebEventController selectEventDetail START");
|
||||
|
||||
|
||||
HashMap<String, Object> paramMap = HttpUtil.getParameterMap(request);
|
||||
HashMap<String, Object> map = new HashMap<String, Object>();
|
||||
StringBuffer errorMsg = new StringBuffer();
|
||||
|
||||
try{
|
||||
|
||||
try {
|
||||
map = webEventService.selectEventDetail(paramMap);
|
||||
log.debug(map + "");
|
||||
}catch (Exception e) {
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
errorMsg.append(e);
|
||||
return null;
|
||||
}finally {
|
||||
if(Constants.OK == map.get("msgCode")) {
|
||||
} finally {
|
||||
if (Constants.OK == map.get("msgCode")) {
|
||||
|
||||
}else{
|
||||
if(null == map.get("msgCode") || ("").equals(map.get("msgCode"))) {
|
||||
} else {
|
||||
if (null == map.get("msgCode") || ("").equals(map.get("msgCode"))) {
|
||||
map.put("msgCode", Constants.FAIL);
|
||||
}
|
||||
|
||||
map.put("success", false);
|
||||
if(null == map.get("msgDesc") || ("").equals(map.get("msgDesc"))) {
|
||||
map.put("msgDesc","정상적으로 수행되지 않았습니다. 관리자에게 문의하시기 바랍니다. (E0029)");
|
||||
if (null == map.get("msgDesc") || ("").equals(map.get("msgDesc"))) {
|
||||
map.put("msgDesc", "정상적으로 수행되지 않았습니다. 관리자에게 문의하시기 바랍니다. (E0029)");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -253,27 +252,26 @@ public class WebEventController extends ManagerDraftAction{
|
||||
insertMap.put("requestValue", String.valueOf(paramMap));
|
||||
insertMap.put("responseValue", String.valueOf(map));
|
||||
;
|
||||
if(("true").equals(String.valueOf(map.get("success")))){
|
||||
if (("true").equals(String.valueOf(map.get("success")))) {
|
||||
insertMap.put("resultCode", "SUCCESS");
|
||||
}else{
|
||||
} else {
|
||||
insertMap.put("resultCode", "ERROR");
|
||||
}
|
||||
|
||||
insertMap.put("resultMsg", String.valueOf(errorMsg));
|
||||
|
||||
// logHistoryService.insertLogHistory(insertMap, visitLogParamMap);
|
||||
// logHistoryService.insertLogHistory(insertMap, visitLogParamMap);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
log.debug("WebEventController selectEventDetail END");
|
||||
|
||||
|
||||
|
||||
return HttpUtil.makeHashToJsonModelAndView(map);
|
||||
}
|
||||
|
||||
@RequestMapping(value="/webevent/selectMakeReservation.do")
|
||||
|
||||
@RequestMapping(value = "/webevent/selectMakeReservation.do")
|
||||
public String selectMakeReservation(HttpSession session, HttpServletRequest request, Model model) {
|
||||
|
||||
log.debug("WebServiceController selectMakeReservation START");
|
||||
@@ -282,214 +280,11 @@ public class WebEventController extends ManagerDraftAction{
|
||||
model.addAttribute("CATEGORY_NO", paramMap.get("CATEGORY_NO"));
|
||||
model.addAttribute("POST_NO", paramMap.get("POST_NO"));
|
||||
model.addAttribute("PROCEDURE_ID", paramMap.get("PROCEDURE_ID"));
|
||||
|
||||
model.addAttribute("EVENT_START_DT", paramMap.get("EVENT_START_DT"));
|
||||
model.addAttribute("EVENT_END_DT", paramMap.get("EVENT_END_DT"));
|
||||
|
||||
log.debug("WebServiceController selectMakeReservation END");
|
||||
|
||||
return "/web/webevent/makeReservation";
|
||||
}
|
||||
|
||||
/**
|
||||
* 시술 목록 상세 조회
|
||||
*
|
||||
* @param request
|
||||
* @param response
|
||||
* @return
|
||||
*/
|
||||
@RequestMapping(value="/webevent/selectReservation.do")
|
||||
public ModelAndView selectReservation(HttpSession session, HttpServletRequest request, HttpServletResponse response) {
|
||||
|
||||
log.debug("WebServiceController selectReservation START");
|
||||
|
||||
HashMap<String, Object> paramMap = HttpUtil.getParameterMap(request);
|
||||
HashMap<String, Object> map = new HashMap<String, Object>();
|
||||
StringBuffer errorMsg = new StringBuffer();
|
||||
|
||||
try{
|
||||
map = webEventService.selectReservation(paramMap);
|
||||
}catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
errorMsg.append(e);
|
||||
return null;
|
||||
}finally {
|
||||
if(Constants.OK == map.get("msgCode")) {
|
||||
|
||||
}else{
|
||||
if(null == map.get("msgCode") || ("").equals(map.get("msgCode"))) {
|
||||
map.put("msgCode", Constants.FAIL);
|
||||
}
|
||||
|
||||
map.put("success", false);
|
||||
if(null == map.get("msgDesc") || ("").equals(map.get("msgDesc"))) {
|
||||
map.put("msgDesc","정상적으로 수행되지 않았습니다. 관리자에게 문의하시기 바랍니다. (E0029)");
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
HashMap<String, Object> visitLogParamMap = RequestLogUtil.getVisitLogParameterMap(request);
|
||||
HashMap<String, Object> insertMap = new HashMap<String, Object>();
|
||||
|
||||
insertMap.put("url", "/webevent/selectReservation.do");
|
||||
insertMap.put("func", "selectListService");
|
||||
insertMap.put("funcName", "예약 조회");
|
||||
insertMap.put("service", "webEventService");
|
||||
insertMap.put("serviceName", "예약 상세");
|
||||
insertMap.put("requestValue", String.valueOf(paramMap));
|
||||
insertMap.put("responseValue", String.valueOf(map));
|
||||
;
|
||||
if(("true").equals(String.valueOf(map.get("success")))){
|
||||
insertMap.put("resultCode", "SUCCESS");
|
||||
}else{
|
||||
insertMap.put("resultCode", "ERROR");
|
||||
}
|
||||
|
||||
insertMap.put("resultMsg", String.valueOf(errorMsg));
|
||||
|
||||
// logHistoryService.insertLogHistory(insertMap, visitLogParamMap);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
log.debug("WebServiceController selectReservation END");
|
||||
|
||||
|
||||
return HttpUtil.makeHashToJsonModelAndView(map);
|
||||
}
|
||||
|
||||
/**
|
||||
* 시술 목록 상세 조회
|
||||
*
|
||||
* @param request
|
||||
* @param response
|
||||
* @return
|
||||
*/
|
||||
@RequestMapping(value="/webevent/selectReservationCnt.do")
|
||||
public ModelAndView selectReservationCnt(HttpSession session, HttpServletRequest request, HttpServletResponse response) {
|
||||
|
||||
log.debug("WebServiceController selectReservationCnt START");
|
||||
|
||||
HashMap<String, Object> paramMap = HttpUtil.getParameterMap(request);
|
||||
HashMap<String, Object> map = new HashMap<String, Object>();
|
||||
StringBuffer errorMsg = new StringBuffer();
|
||||
|
||||
try{
|
||||
map = webEventService.selectReservationCnt(paramMap);
|
||||
}catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
errorMsg.append(e);
|
||||
return null;
|
||||
}finally {
|
||||
if(Constants.OK == map.get("msgCode")) {
|
||||
|
||||
}else{
|
||||
if(null == map.get("msgCode") || ("").equals(map.get("msgCode"))) {
|
||||
map.put("msgCode", Constants.FAIL);
|
||||
}
|
||||
|
||||
map.put("success", false);
|
||||
if(null == map.get("msgDesc") || ("").equals(map.get("msgDesc"))) {
|
||||
map.put("msgDesc","정상적으로 수행되지 않았습니다. 관리자에게 문의하시기 바랍니다. (E0029)");
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
HashMap<String, Object> visitLogParamMap = RequestLogUtil.getVisitLogParameterMap(request);
|
||||
HashMap<String, Object> insertMap = new HashMap<String, Object>();
|
||||
|
||||
insertMap.put("url", "/webevent/selectReservationCnt.do");
|
||||
insertMap.put("func", "selectReservationCnt");
|
||||
insertMap.put("funcName", "예약 조회");
|
||||
insertMap.put("service", "webEventService");
|
||||
insertMap.put("serviceName", "예약 상세");
|
||||
insertMap.put("requestValue", String.valueOf(paramMap));
|
||||
insertMap.put("responseValue", String.valueOf(map));
|
||||
;
|
||||
if(("true").equals(String.valueOf(map.get("success")))){
|
||||
insertMap.put("resultCode", "SUCCESS");
|
||||
}else{
|
||||
insertMap.put("resultCode", "ERROR");
|
||||
}
|
||||
|
||||
insertMap.put("resultMsg", String.valueOf(errorMsg));
|
||||
|
||||
// logHistoryService.insertLogHistory(insertMap, visitLogParamMap);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
log.debug("WebServiceController selectReservationCnt END");
|
||||
|
||||
|
||||
return HttpUtil.makeHashToJsonModelAndView(map);
|
||||
}
|
||||
|
||||
/**
|
||||
* 예약 저장
|
||||
*
|
||||
* @param request
|
||||
* @param response
|
||||
* @return
|
||||
*/
|
||||
@RequestMapping(value="/webevent/insertReservation.do")
|
||||
public ModelAndView insertReservation(HttpSession session, HttpServletRequest request, HttpServletResponse response) {
|
||||
|
||||
log.debug("WebServiceController insertReservation START");
|
||||
|
||||
HashMap<String, Object> paramMap = HttpUtil.getParameterMap(request);
|
||||
HashMap<String, Object> map = new HashMap<String, Object>();
|
||||
StringBuffer errorMsg = new StringBuffer();
|
||||
|
||||
try{
|
||||
map = webEventService.insertReservation(paramMap);
|
||||
log.debug(map + "TEST");
|
||||
}catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
errorMsg.append(e);
|
||||
return null;
|
||||
}finally {
|
||||
if(Constants.OK == map.get("msgCode")) {
|
||||
|
||||
}else{
|
||||
if(null == map.get("msgCode") || ("").equals(map.get("msgCode"))) {
|
||||
map.put("msgCode", Constants.FAIL);
|
||||
}
|
||||
|
||||
map.put("success", false);
|
||||
if(null == map.get("msgDesc") || ("").equals(map.get("msgDesc"))) {
|
||||
map.put("msgDesc","정상적으로 수행되지 않았습니다. 관리자에게 문의하시기 바랍니다. (E0029)");
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
HashMap<String, Object> visitLogParamMap = RequestLogUtil.getVisitLogParameterMap(request);
|
||||
HashMap<String, Object> insertMap = new HashMap<String, Object>();
|
||||
|
||||
insertMap.put("url", "/webevent/insertReservation.do");
|
||||
insertMap.put("func", "selectReservationCnt");
|
||||
insertMap.put("funcName", "예약 저장");
|
||||
insertMap.put("service", "webEventService");
|
||||
insertMap.put("serviceName", "예약 저장");
|
||||
insertMap.put("requestValue", String.valueOf(paramMap));
|
||||
insertMap.put("responseValue", String.valueOf(map));
|
||||
;
|
||||
if(("true").equals(String.valueOf(map.get("success")))){
|
||||
insertMap.put("resultCode", "SUCCESS");
|
||||
}else{
|
||||
insertMap.put("resultCode", "ERROR");
|
||||
}
|
||||
|
||||
insertMap.put("resultMsg", String.valueOf(errorMsg));
|
||||
|
||||
// logHistoryService.insertLogHistory(insertMap, visitLogParamMap);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
log.debug("WebServiceController insertReservation END");
|
||||
|
||||
|
||||
return HttpUtil.makeHashToJsonModelAndView(map);
|
||||
return "/web/service/makeReservation";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,32 +5,31 @@ import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpSession;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
|
||||
@Slf4j
|
||||
@Controller
|
||||
public class WebHomeController extends ManagerDraftAction{
|
||||
public class WebHomeController extends ManagerDraftAction {
|
||||
|
||||
/**
|
||||
* 홈 화면으로 이동.
|
||||
*
|
||||
* @param request
|
||||
* @param response
|
||||
* @param response
|
||||
* @return
|
||||
*/
|
||||
@RequestMapping(value="/")
|
||||
public String homeIntro(HttpSession session,HttpServletRequest request) {
|
||||
|
||||
@GetMapping("/")
|
||||
public String homeIntro(HttpSession session, HttpServletRequest request) {
|
||||
|
||||
log.debug("WebHomeController homeIntro START");
|
||||
|
||||
log.debug("WebHomeController homeIntro END");
|
||||
return "/intro";
|
||||
return "redirect:/webservice/selectServiceDetailIntro.do?categoryDivCd=01&categoryNo=1&postNo=1";
|
||||
}
|
||||
|
||||
@RequestMapping(value="/index")
|
||||
public String homeIndex(HttpSession session,HttpServletRequest request) {
|
||||
|
||||
|
||||
@GetMapping("/index")
|
||||
public String homeIndex(HttpSession session, HttpServletRequest request) {
|
||||
|
||||
log.debug("WebHomeController homeIndex START");
|
||||
|
||||
log.debug("WebHomeController homeIndex END");
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
package com.madeuhome.controller.web.webreview;
|
||||
|
||||
import com.madeuhome.constants.Constants;
|
||||
import com.madeuhome.init.ManagerDraftAction;
|
||||
import com.madeuhome.service.web.webreview.WebReviewService;
|
||||
import com.madeuhome.util.HttpUtil;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.servlet.http.HttpSession;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
@Slf4j
|
||||
@Controller
|
||||
public class WebReviewController extends ManagerDraftAction {
|
||||
|
||||
@Autowired
|
||||
private WebReviewService webReviewService;
|
||||
|
||||
/**
|
||||
* 시술후기 리스트 화면으로 이동
|
||||
*/
|
||||
@RequestMapping(value = "/webreview/selectListProcedureReviewIntro.do")
|
||||
public String selectListProcedureReviewIntro(HttpSession session, HttpServletRequest request) {
|
||||
log.debug("WebReviewController selectListProcedureReviewIntro START");
|
||||
log.debug("WebReviewController selectListProcedureReviewIntro END");
|
||||
return "/web/webreview/procedureReviewSelectList";
|
||||
}
|
||||
|
||||
/**
|
||||
* 시술후기 리스트 조회 (AJAX)
|
||||
*/
|
||||
@RequestMapping(value = "/webreview/selectListProcedureReview.do")
|
||||
public ModelAndView selectListProcedureReview(HttpSession session, HttpServletRequest request,
|
||||
HttpServletResponse response) {
|
||||
log.debug("WebReviewController selectListProcedureReview START");
|
||||
|
||||
HashMap<String, Object> paramMap = HttpUtil.getParameterMap(request);
|
||||
HashMap<String, Object> map = new HashMap<String, Object>();
|
||||
StringBuffer errorMsg = new StringBuffer();
|
||||
|
||||
try {
|
||||
map = webReviewService.selectListProcedureReview(paramMap);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
errorMsg.append(e);
|
||||
return null;
|
||||
} finally {
|
||||
if (Constants.OK == map.get("msgCode")) {
|
||||
} else {
|
||||
if (null == map.get("msgCode") || ("").equals(map.get("msgCode"))) {
|
||||
map.put("msgCode", Constants.FAIL);
|
||||
}
|
||||
map.put("success", false);
|
||||
if (null == map.get("msgDesc") || ("").equals(map.get("msgDesc"))) {
|
||||
map.put("msgDesc", "정상적으로 수행되지 않았습니다. 관리자에게 문의하시기 바랍니다.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log.debug("WebReviewController selectListProcedureReview END");
|
||||
return HttpUtil.makeHashToJsonModelAndView(map);
|
||||
}
|
||||
|
||||
/**
|
||||
* 시술후기 상세 화면으로 이동
|
||||
*/
|
||||
@RequestMapping(value = "/webreview/selectProcedureReviewIntro.do")
|
||||
public String selectProcedureReviewIntro(HttpSession session, HttpServletRequest request, Model model) {
|
||||
log.debug("WebReviewController selectProcedureReviewIntro START");
|
||||
HashMap<String, Object> paramMap = HttpUtil.getParameterMap(request);
|
||||
model.addAttribute("muProcedureReviewId", paramMap.get("muProcedureReviewId"));
|
||||
log.debug("WebReviewController selectProcedureReviewIntro END");
|
||||
return "/web/webreview/procedureReviewSelect";
|
||||
}
|
||||
|
||||
/**
|
||||
* 시술후기 상세 조회 (AJAX)
|
||||
*/
|
||||
@RequestMapping(value = "/webreview/selectProcedureReview.do")
|
||||
public ModelAndView selectProcedureReview(HttpSession session, HttpServletRequest request,
|
||||
HttpServletResponse response) {
|
||||
log.debug("WebReviewController selectProcedureReview START");
|
||||
|
||||
HashMap<String, Object> paramMap = HttpUtil.getParameterMap(request);
|
||||
HashMap<String, Object> map = new HashMap<String, Object>();
|
||||
StringBuffer errorMsg = new StringBuffer();
|
||||
|
||||
try {
|
||||
map = webReviewService.selectProcedureReview(paramMap);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
errorMsg.append(e);
|
||||
return null;
|
||||
} finally {
|
||||
if (Constants.OK == map.get("msgCode")) {
|
||||
} else {
|
||||
if (null == map.get("msgCode") || ("").equals(map.get("msgCode"))) {
|
||||
map.put("msgCode", Constants.FAIL);
|
||||
}
|
||||
map.put("success", false);
|
||||
if (null == map.get("msgDesc") || ("").equals(map.get("msgDesc"))) {
|
||||
map.put("msgDesc", "정상적으로 수행되지 않았습니다. 관리자에게 문의하시기 바랍니다.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log.debug("WebReviewController selectProcedureReview END");
|
||||
return HttpUtil.makeHashToJsonModelAndView(map);
|
||||
}
|
||||
}
|
||||
@@ -25,13 +25,12 @@ import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.servlet.http.HttpSession;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
|
||||
@Slf4j
|
||||
@Controller
|
||||
public class WebServiceController extends ManagerDraftAction{
|
||||
public class WebServiceController extends ManagerDraftAction {
|
||||
@Autowired
|
||||
private WebServiceService webServiceService;
|
||||
|
||||
|
||||
/**
|
||||
* 시술관리 목록으로 이동.
|
||||
*
|
||||
@@ -39,16 +38,16 @@ public class WebServiceController extends ManagerDraftAction{
|
||||
* @param response
|
||||
* @return
|
||||
*/
|
||||
@RequestMapping(value="/webservice/selectServiceIntro.do")
|
||||
public String selectListServiceIntro(HttpSession session,HttpServletRequest request) {
|
||||
@RequestMapping(value = "/webservice/selectServiceIntro.do")
|
||||
public String selectListServiceIntro(HttpSession session, HttpServletRequest request) {
|
||||
|
||||
log.debug("WebServiceController selectListServiceIntro START");
|
||||
|
||||
log.debug("WebServiceController selectListServiceIntro END");
|
||||
|
||||
|
||||
return "/web/service/serviceInfo";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 시술관리 카테고리 목록 조회
|
||||
*
|
||||
@@ -56,33 +55,34 @@ public class WebServiceController extends ManagerDraftAction{
|
||||
* @param response
|
||||
* @return
|
||||
*/
|
||||
@RequestMapping(value="/webservice/selectListCategory.do")
|
||||
public ModelAndView selectListCategory(HttpSession session,HttpServletRequest request, HttpServletResponse response) {
|
||||
@RequestMapping(value = "/webservice/selectListCategory.do")
|
||||
public ModelAndView selectListCategory(HttpSession session, HttpServletRequest request,
|
||||
HttpServletResponse response) {
|
||||
|
||||
log.debug("WebServiceController selectListCategory START");
|
||||
|
||||
|
||||
HashMap<String, Object> paramMap = HttpUtil.getParameterMap(request);
|
||||
HashMap<String, Object> map = new HashMap<String, Object>();
|
||||
StringBuffer errorMsg = new StringBuffer();
|
||||
|
||||
try{
|
||||
|
||||
try {
|
||||
map = webServiceService.selectListWebCategory(paramMap);
|
||||
log.debug(map + "");
|
||||
}catch (Exception e) {
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
errorMsg.append(e);
|
||||
return null;
|
||||
}finally {
|
||||
if(Constants.OK == map.get("msgCode")) {
|
||||
} finally {
|
||||
if (Constants.OK == map.get("msgCode")) {
|
||||
|
||||
}else{
|
||||
if(null == map.get("msgCode") || ("").equals(map.get("msgCode"))) {
|
||||
} else {
|
||||
if (null == map.get("msgCode") || ("").equals(map.get("msgCode"))) {
|
||||
map.put("msgCode", Constants.FAIL);
|
||||
}
|
||||
|
||||
map.put("success", false);
|
||||
if(null == map.get("msgDesc") || ("").equals(map.get("msgDesc"))) {
|
||||
map.put("msgDesc","정상적으로 수행되지 않았습니다. 관리자에게 문의하시기 바랍니다. (E0029)");
|
||||
if (null == map.get("msgDesc") || ("").equals(map.get("msgDesc"))) {
|
||||
map.put("msgDesc", "정상적으로 수행되지 않았습니다. 관리자에게 문의하시기 바랍니다. (E0029)");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,27 +98,26 @@ public class WebServiceController extends ManagerDraftAction{
|
||||
insertMap.put("requestValue", String.valueOf(paramMap));
|
||||
insertMap.put("responseValue", String.valueOf(map));
|
||||
;
|
||||
if(("true").equals(String.valueOf(map.get("success")))){
|
||||
if (("true").equals(String.valueOf(map.get("success")))) {
|
||||
insertMap.put("resultCode", "SUCCESS");
|
||||
}else{
|
||||
} else {
|
||||
insertMap.put("resultCode", "ERROR");
|
||||
}
|
||||
|
||||
insertMap.put("resultMsg", String.valueOf(errorMsg));
|
||||
insertMap.put("muMemberId", paramMap.get("muMemberId"));
|
||||
|
||||
// logHistoryService.insertLogHistory(insertMap, visitLogParamMap);
|
||||
// logHistoryService.insertLogHistory(insertMap, visitLogParamMap);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
log.debug("WebServiceController selectListCategory END");
|
||||
|
||||
|
||||
|
||||
return HttpUtil.makeHashToJsonModelAndView(map);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 카테고리 별 시술 목록 조회
|
||||
*
|
||||
@@ -126,28 +125,28 @@ public class WebServiceController extends ManagerDraftAction{
|
||||
* @param response
|
||||
* @return
|
||||
*/
|
||||
@PostMapping(value="/webservice/selectListService.do")
|
||||
@PostMapping(value = "/webservice/selectListService.do")
|
||||
@ResponseBody
|
||||
public ResponseEntity<Map<String, Object>> selectListService(@RequestParam("categoryNo") String categoryNo) {
|
||||
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
|
||||
try {
|
||||
HashMap<String, Object> paramMap = new HashMap<>();
|
||||
paramMap.put("categoryNo", categoryNo);
|
||||
|
||||
Map<String, Object> result = webServiceService.selectListWebService(paramMap);
|
||||
return ResponseEntity.ok(result);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("서비스 조회 실패", e);
|
||||
response.put("msgCode", Constants.FAIL);
|
||||
response.put("msgDesc", e.getMessage());
|
||||
response.put("rows", new ArrayList<>());
|
||||
return ResponseEntity.ok(response);
|
||||
}
|
||||
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
|
||||
try {
|
||||
HashMap<String, Object> paramMap = new HashMap<>();
|
||||
paramMap.put("categoryNo", categoryNo);
|
||||
|
||||
Map<String, Object> result = webServiceService.selectListWebService(paramMap);
|
||||
return ResponseEntity.ok(result);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("서비스 조회 실패", e);
|
||||
response.put("msgCode", Constants.FAIL);
|
||||
response.put("msgDesc", e.getMessage());
|
||||
response.put("rows", new ArrayList<>());
|
||||
return ResponseEntity.ok(response);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 시술관리 상세로 이동.
|
||||
*
|
||||
@@ -155,19 +154,19 @@ public class WebServiceController extends ManagerDraftAction{
|
||||
* @param response
|
||||
* @return
|
||||
*/
|
||||
@RequestMapping(value="/webservice/selectServiceDetailIntro.do")
|
||||
@RequestMapping(value = "/webservice/selectServiceDetailIntro.do")
|
||||
public String selectServiceDetailIntro(HttpSession session, HttpServletRequest request, Model model) {
|
||||
|
||||
log.debug("WebServiceController selectServiceDetailIntro START");
|
||||
HashMap<String, Object> paramMap = HttpUtil.getParameterMap(request);
|
||||
model.addAttribute("CATEGORY_DIV_CD", paramMap.get("categoryDivCd"));
|
||||
model.addAttribute("CATEGORY_NO", paramMap.get("categoryNo"));
|
||||
model.addAttribute("POST_NO", paramMap.get("postNo"));
|
||||
model.addAttribute("POST_NO", paramMap.get("postNo"));
|
||||
log.debug("WebServiceController selectServiceDetailIntro END");
|
||||
|
||||
|
||||
return "/web/service/webServiceDetail";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 시술 목록 상세 조회
|
||||
*
|
||||
@@ -175,32 +174,33 @@ public class WebServiceController extends ManagerDraftAction{
|
||||
* @param response
|
||||
* @return
|
||||
*/
|
||||
@RequestMapping(value="/webservice/selectServiceDetail.do")
|
||||
public ModelAndView selectServiceDetail(HttpSession session, HttpServletRequest request, HttpServletResponse response) {
|
||||
@RequestMapping(value = "/webservice/selectServiceDetail.do")
|
||||
public ModelAndView selectServiceDetail(HttpSession session, HttpServletRequest request,
|
||||
HttpServletResponse response) {
|
||||
|
||||
log.debug("WebServiceController selectServiceDetail START");
|
||||
|
||||
|
||||
HashMap<String, Object> paramMap = HttpUtil.getParameterMap(request);
|
||||
HashMap<String, Object> map = new HashMap<String, Object>();
|
||||
StringBuffer errorMsg = new StringBuffer();
|
||||
|
||||
try{
|
||||
map = webServiceService.selectServiceDetail(paramMap);
|
||||
}catch (Exception e) {
|
||||
|
||||
try {
|
||||
map = webServiceService.selectServiceDetail(paramMap);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
errorMsg.append(e);
|
||||
return null;
|
||||
}finally {
|
||||
if(Constants.OK == map.get("msgCode")) {
|
||||
} finally {
|
||||
if (Constants.OK == map.get("msgCode")) {
|
||||
|
||||
}else{
|
||||
if(null == map.get("msgCode") || ("").equals(map.get("msgCode"))) {
|
||||
} else {
|
||||
if (null == map.get("msgCode") || ("").equals(map.get("msgCode"))) {
|
||||
map.put("msgCode", Constants.FAIL);
|
||||
}
|
||||
|
||||
map.put("success", false);
|
||||
if(null == map.get("msgDesc") || ("").equals(map.get("msgDesc"))) {
|
||||
map.put("msgDesc","정상적으로 수행되지 않았습니다. 관리자에게 문의하시기 바랍니다. (E0029)");
|
||||
if (null == map.get("msgDesc") || ("").equals(map.get("msgDesc"))) {
|
||||
map.put("msgDesc", "정상적으로 수행되지 않았습니다. 관리자에게 문의하시기 바랍니다. (E0029)");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -216,243 +216,23 @@ public class WebServiceController extends ManagerDraftAction{
|
||||
insertMap.put("requestValue", String.valueOf(paramMap));
|
||||
insertMap.put("responseValue", String.valueOf(map));
|
||||
;
|
||||
if(("true").equals(String.valueOf(map.get("success")))){
|
||||
if (("true").equals(String.valueOf(map.get("success")))) {
|
||||
insertMap.put("resultCode", "SUCCESS");
|
||||
}else{
|
||||
} else {
|
||||
insertMap.put("resultCode", "ERROR");
|
||||
}
|
||||
|
||||
insertMap.put("resultMsg", String.valueOf(errorMsg));
|
||||
|
||||
// logHistoryService.insertLogHistory(insertMap, visitLogParamMap);
|
||||
// logHistoryService.insertLogHistory(insertMap, visitLogParamMap);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
log.debug("WebServiceController selectServiceDetail END");
|
||||
|
||||
|
||||
|
||||
return HttpUtil.makeHashToJsonModelAndView(map);
|
||||
}
|
||||
|
||||
@RequestMapping(value="/webservice/selectMakeReservation.do")
|
||||
public String selectMakeReservation(HttpSession session, HttpServletRequest request, Model model) {
|
||||
|
||||
log.debug("WebServiceController selectMakeReservation START");
|
||||
HashMap<String, Object> paramMap = HttpUtil.getParameterMap(request);
|
||||
model.addAttribute("CATEGORY_DIV_CD", paramMap.get("CATEGORY_DIV_CD"));
|
||||
model.addAttribute("CATEGORY_NO", paramMap.get("CATEGORY_NO"));
|
||||
model.addAttribute("POST_NO", paramMap.get("POST_NO"));
|
||||
model.addAttribute("PROCEDURE_ID", paramMap.get("PROCEDURE_ID"));
|
||||
|
||||
log.debug("WebServiceController selectMakeReservation END");
|
||||
|
||||
return "/web/service/makeReservation";
|
||||
}
|
||||
|
||||
/**
|
||||
* 시술 목록 상세 조회
|
||||
*
|
||||
* @param request
|
||||
* @param response
|
||||
* @return
|
||||
*/
|
||||
@RequestMapping(value="/webservice/selectReservation.do")
|
||||
public ModelAndView selectReservation(HttpSession session, HttpServletRequest request, HttpServletResponse response) {
|
||||
|
||||
log.debug("WebServiceController selectReservation START");
|
||||
|
||||
HashMap<String, Object> paramMap = HttpUtil.getParameterMap(request);
|
||||
HashMap<String, Object> map = new HashMap<String, Object>();
|
||||
StringBuffer errorMsg = new StringBuffer();
|
||||
|
||||
try{
|
||||
map = webServiceService.selectReservation(paramMap);
|
||||
}catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
errorMsg.append(e);
|
||||
return null;
|
||||
}finally {
|
||||
if(Constants.OK == map.get("msgCode")) {
|
||||
|
||||
}else{
|
||||
if(null == map.get("msgCode") || ("").equals(map.get("msgCode"))) {
|
||||
map.put("msgCode", Constants.FAIL);
|
||||
}
|
||||
|
||||
map.put("success", false);
|
||||
if(null == map.get("msgDesc") || ("").equals(map.get("msgDesc"))) {
|
||||
map.put("msgDesc","정상적으로 수행되지 않았습니다. 관리자에게 문의하시기 바랍니다. (E0029)");
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
HashMap<String, Object> visitLogParamMap = RequestLogUtil.getVisitLogParameterMap(request);
|
||||
HashMap<String, Object> insertMap = new HashMap<String, Object>();
|
||||
|
||||
insertMap.put("url", "/webservice/selectReservation.do");
|
||||
insertMap.put("func", "selectListService");
|
||||
insertMap.put("funcName", "예약 조회");
|
||||
insertMap.put("service", "webServiceService");
|
||||
insertMap.put("serviceName", "예약 상세");
|
||||
insertMap.put("requestValue", String.valueOf(paramMap));
|
||||
insertMap.put("responseValue", String.valueOf(map));
|
||||
;
|
||||
if(("true").equals(String.valueOf(map.get("success")))){
|
||||
insertMap.put("resultCode", "SUCCESS");
|
||||
}else{
|
||||
insertMap.put("resultCode", "ERROR");
|
||||
}
|
||||
|
||||
insertMap.put("resultMsg", String.valueOf(errorMsg));
|
||||
|
||||
// logHistoryService.insertLogHistory(insertMap, visitLogParamMap);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
log.debug("WebServiceController selectReservation END");
|
||||
|
||||
|
||||
return HttpUtil.makeHashToJsonModelAndView(map);
|
||||
}
|
||||
|
||||
/**
|
||||
* 시술 목록 상세 조회
|
||||
*
|
||||
* @param request
|
||||
* @param response
|
||||
* @return
|
||||
*/
|
||||
@RequestMapping(value="/webservice/selectReservationCnt.do")
|
||||
public ModelAndView selectReservationCnt(HttpSession session, HttpServletRequest request, HttpServletResponse response) {
|
||||
|
||||
log.debug("WebServiceController selectReservationCnt START");
|
||||
|
||||
HashMap<String, Object> paramMap = HttpUtil.getParameterMap(request);
|
||||
HashMap<String, Object> map = new HashMap<String, Object>();
|
||||
StringBuffer errorMsg = new StringBuffer();
|
||||
|
||||
try{
|
||||
map = webServiceService.selectReservationCnt(paramMap);
|
||||
}catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
errorMsg.append(e);
|
||||
return null;
|
||||
}finally {
|
||||
if(Constants.OK == map.get("msgCode")) {
|
||||
|
||||
}else{
|
||||
if(null == map.get("msgCode") || ("").equals(map.get("msgCode"))) {
|
||||
map.put("msgCode", Constants.FAIL);
|
||||
}
|
||||
|
||||
map.put("success", false);
|
||||
if(null == map.get("msgDesc") || ("").equals(map.get("msgDesc"))) {
|
||||
map.put("msgDesc","정상적으로 수행되지 않았습니다. 관리자에게 문의하시기 바랍니다. (E0029)");
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
HashMap<String, Object> visitLogParamMap = RequestLogUtil.getVisitLogParameterMap(request);
|
||||
HashMap<String, Object> insertMap = new HashMap<String, Object>();
|
||||
|
||||
insertMap.put("url", "/webservice/selectReservationCnt.do");
|
||||
insertMap.put("func", "selectReservationCnt");
|
||||
insertMap.put("funcName", "예약 조회");
|
||||
insertMap.put("service", "webServiceService");
|
||||
insertMap.put("serviceName", "예약 상세");
|
||||
insertMap.put("requestValue", String.valueOf(paramMap));
|
||||
insertMap.put("responseValue", String.valueOf(map));
|
||||
;
|
||||
if(("true").equals(String.valueOf(map.get("success")))){
|
||||
insertMap.put("resultCode", "SUCCESS");
|
||||
}else{
|
||||
insertMap.put("resultCode", "ERROR");
|
||||
}
|
||||
|
||||
insertMap.put("resultMsg", String.valueOf(errorMsg));
|
||||
|
||||
// logHistoryService.insertLogHistory(insertMap, visitLogParamMap);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
log.debug("WebServiceController selectReservationCnt END");
|
||||
|
||||
|
||||
return HttpUtil.makeHashToJsonModelAndView(map);
|
||||
}
|
||||
|
||||
/**
|
||||
* 예약 저장
|
||||
*
|
||||
* @param request
|
||||
* @param response
|
||||
* @return
|
||||
*/
|
||||
@RequestMapping(value="/webservice/insertReservation.do")
|
||||
public ModelAndView insertReservation(HttpSession session, HttpServletRequest request, HttpServletResponse response) {
|
||||
|
||||
log.debug("WebServiceController insertReservation START");
|
||||
|
||||
HashMap<String, Object> paramMap = HttpUtil.getParameterMap(request);
|
||||
HashMap<String, Object> map = new HashMap<String, Object>();
|
||||
StringBuffer errorMsg = new StringBuffer();
|
||||
|
||||
try{
|
||||
map = webServiceService.insertReservation(paramMap);
|
||||
log.debug(map + "TEST");
|
||||
}catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
errorMsg.append(e);
|
||||
return null;
|
||||
}finally {
|
||||
if(Constants.OK == map.get("msgCode")) {
|
||||
|
||||
}else{
|
||||
if(null == map.get("msgCode") || ("").equals(map.get("msgCode"))) {
|
||||
map.put("msgCode", Constants.FAIL);
|
||||
}
|
||||
|
||||
map.put("success", false);
|
||||
if(null == map.get("msgDesc") || ("").equals(map.get("msgDesc"))) {
|
||||
map.put("msgDesc","정상적으로 수행되지 않았습니다. 관리자에게 문의하시기 바랍니다. (E0029)");
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
HashMap<String, Object> visitLogParamMap = RequestLogUtil.getVisitLogParameterMap(request);
|
||||
HashMap<String, Object> insertMap = new HashMap<String, Object>();
|
||||
|
||||
insertMap.put("url", "/webservice/insertReservation.do");
|
||||
insertMap.put("func", "selectReservationCnt");
|
||||
insertMap.put("funcName", "예약 저장");
|
||||
insertMap.put("service", "webServiceService");
|
||||
insertMap.put("serviceName", "예약 저장");
|
||||
insertMap.put("requestValue", String.valueOf(paramMap));
|
||||
insertMap.put("responseValue", String.valueOf(map));
|
||||
;
|
||||
if(("true").equals(String.valueOf(map.get("success")))){
|
||||
insertMap.put("resultCode", "SUCCESS");
|
||||
}else{
|
||||
insertMap.put("resultCode", "ERROR");
|
||||
}
|
||||
|
||||
insertMap.put("resultMsg", String.valueOf(errorMsg));
|
||||
|
||||
// logHistoryService.insertLogHistory(insertMap, visitLogParamMap);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
log.debug("WebServiceController insertReservation END");
|
||||
|
||||
|
||||
return HttpUtil.makeHashToJsonModelAndView(map);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
package com.madeuhome.dao.web.webreview;
|
||||
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import org.mybatis.spring.SqlSessionTemplate;
|
||||
import org.mybatis.spring.support.SqlSessionDaoSupport;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Repository
|
||||
public class WebReviewSqlMapDAO extends SqlSessionDaoSupport {
|
||||
|
||||
@Autowired
|
||||
private SqlSessionTemplate sqlSessionTemplate;
|
||||
|
||||
@PostConstruct
|
||||
void init() {
|
||||
setSqlSessionTemplate(sqlSessionTemplate);
|
||||
}
|
||||
|
||||
/**
|
||||
* 시술후기 전체 건수 조회
|
||||
*/
|
||||
public Map<String, Object> selectTotalProcedureReviewCount(HashMap<String, Object> paramMap)
|
||||
throws DataAccessException {
|
||||
logger.debug("WebReviewSqlMapDAO selectTotalProcedureReviewCount START");
|
||||
String sqlId = "WebReview.selectTotalProcedureReviewCount";
|
||||
logger.debug("WebReviewSqlMapDAO selectTotalProcedureReviewCount END");
|
||||
return getSqlSession().selectOne(sqlId, paramMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* 시술후기 리스트 조회
|
||||
*/
|
||||
public List<Map<String, Object>> selectListProcedureReview(HashMap<String, Object> paramMap)
|
||||
throws DataAccessException {
|
||||
logger.debug("WebReviewSqlMapDAO selectListProcedureReview START");
|
||||
String sqlId = "WebReview.selectListProcedureReview";
|
||||
logger.debug("WebReviewSqlMapDAO selectListProcedureReview END");
|
||||
return getSqlSession().selectList(sqlId, paramMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* 시술후기 상세 조회
|
||||
*/
|
||||
public Map<String, Object> selectProcedureReview(HashMap<String, Object> paramMap)
|
||||
throws DataAccessException {
|
||||
logger.debug("WebReviewSqlMapDAO selectProcedureReview START");
|
||||
String sqlId = "WebReview.selectProcedureReview";
|
||||
logger.debug("WebReviewSqlMapDAO selectProcedureReview END");
|
||||
return getSqlSession().selectOne(sqlId, paramMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* 시술후기 조회수 증가
|
||||
*/
|
||||
public void updateViewCount(HashMap<String, Object> paramMap)
|
||||
throws DataAccessException {
|
||||
logger.debug("WebReviewSqlMapDAO updateViewCount START");
|
||||
String sqlId = "WebReview.updateViewCount";
|
||||
getSqlSession().update(sqlId, paramMap);
|
||||
logger.debug("WebReviewSqlMapDAO updateViewCount END");
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,7 @@ import org.springframework.stereotype.Repository;
|
||||
import jakarta.annotation.PostConstruct;
|
||||
|
||||
@Repository
|
||||
public class WebServiceSqlMapDAO extends SqlSessionDaoSupport{
|
||||
public class WebServiceSqlMapDAO extends SqlSessionDaoSupport {
|
||||
|
||||
@Autowired
|
||||
private SqlSessionTemplate sqlSessionTemplate;
|
||||
@@ -22,124 +22,74 @@ public class WebServiceSqlMapDAO extends SqlSessionDaoSupport{
|
||||
void init() {
|
||||
setSqlSessionTemplate(sqlSessionTemplate);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 시술관리 정보 카테고리 리스트 조회 (List)
|
||||
*
|
||||
* @param Map
|
||||
* @return
|
||||
* @throws DataAccessException
|
||||
* @throws DataAccessException
|
||||
*/
|
||||
public List<Map<String, Object>> selectListWebCategory(HashMap<String, Object> paramMap) throws DataAccessException {
|
||||
public List<Map<String, Object>> selectListWebCategory(HashMap<String, Object> paramMap)
|
||||
throws DataAccessException {
|
||||
logger.debug("WebServiceSqlMapDAO selectListWebCategory START");
|
||||
|
||||
|
||||
String sqlId = "WebService.selectListWebCategory";
|
||||
|
||||
|
||||
logger.debug("WebServiceSqlMapDAO selectListWebCategory END");
|
||||
|
||||
|
||||
return getSqlSession().selectList(sqlId, paramMap);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 시술관리 정보 시술 리스트 조회 (List)
|
||||
*
|
||||
* @param Map
|
||||
* @return
|
||||
* @throws DataAccessException
|
||||
* @throws DataAccessException
|
||||
*/
|
||||
public List<Map<String, Object>> selectListWebService(HashMap<String, Object> paramMap) throws DataAccessException {
|
||||
public List<Map<String, Object>> selectListWebService(HashMap<String, Object> paramMap) throws DataAccessException {
|
||||
logger.debug("WebServiceSqlMapDAO selectListWebService START");
|
||||
|
||||
|
||||
String sqlId = "WebService.selectListWebService";
|
||||
|
||||
|
||||
logger.debug("WebServiceSqlMapDAO selectListWebService END");
|
||||
|
||||
|
||||
return getSqlSession().selectList(sqlId, paramMap);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 시술관리 정보 시술 상세 조회
|
||||
* 시술관리 정보 시술 상세 조회
|
||||
*
|
||||
* @param Map
|
||||
* @return
|
||||
* @throws DataAccessException
|
||||
* @throws DataAccessException
|
||||
*/
|
||||
public Map<String, Object> selectServiceDetail(HashMap<String, Object> paramMap) throws DataAccessException {
|
||||
public Map<String, Object> selectServiceDetail(HashMap<String, Object> paramMap) throws DataAccessException {
|
||||
logger.debug("WebServiceSqlMapDAO selectServiceDetail START");
|
||||
|
||||
|
||||
String sqlId = "WebService.selectServiceDetail";
|
||||
|
||||
|
||||
logger.debug("WebServiceSqlMapDAO selectServiceDetail END");
|
||||
|
||||
|
||||
return getSqlSession().selectOne(sqlId, paramMap);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 시술관리 정보 시술 목록 조회 (List)
|
||||
*
|
||||
* @param Map
|
||||
* @return
|
||||
* @throws DataAccessException
|
||||
* @throws DataAccessException
|
||||
*/
|
||||
public List<Map<String, Object>> selectListService(HashMap<String, Object> paramMap) throws DataAccessException {
|
||||
public List<Map<String, Object>> selectListService(HashMap<String, Object> paramMap) throws DataAccessException {
|
||||
logger.debug("WebServiceSqlMapDAO selectListService START");
|
||||
|
||||
|
||||
String sqlId = "WebService.selectListService";
|
||||
|
||||
|
||||
logger.debug("WebServiceSqlMapDAO selectListService END");
|
||||
|
||||
|
||||
return getSqlSession().selectList(sqlId, paramMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* 예약 상세 조회
|
||||
*
|
||||
* @param Map
|
||||
* @return
|
||||
* @throws DataAccessException
|
||||
*/
|
||||
public Map<String, Object> selectReservationCnt(HashMap<String, Object> paramMap) throws DataAccessException {
|
||||
logger.debug("WebServiceSqlMapDAO selectReservationCnt START");
|
||||
|
||||
String sqlId = "WebService.selectReservationCnt";
|
||||
|
||||
logger.debug("WebServiceSqlMapDAO selectReservationCnt END");
|
||||
|
||||
return getSqlSession().selectOne(sqlId, paramMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* 예약 시술 정보 조회
|
||||
*
|
||||
* @param Map
|
||||
* @return
|
||||
* @throws DataAccessException
|
||||
*/
|
||||
public Map<String, Object> selectReservationService(HashMap<String, Object> paramMap) throws DataAccessException {
|
||||
logger.debug("WebServiceSqlMapDAO selectReservationService START");
|
||||
|
||||
String sqlId = "WebService.selectReservationService";
|
||||
|
||||
logger.debug("WebServiceSqlMapDAO selectReservationService END");
|
||||
|
||||
return getSqlSession().selectOne(sqlId, paramMap);
|
||||
}
|
||||
/**
|
||||
* 예약 정보 저장
|
||||
*
|
||||
* @param Map
|
||||
* @return
|
||||
* @throws DataAccessException
|
||||
*/
|
||||
public Map<String, Object> insertReservation(HashMap<String, Object> paramMap) throws DataAccessException {
|
||||
logger.debug("WebServiceSqlMapDAO insertReservation START");
|
||||
|
||||
String sqlId = "WebService.insertReservation";
|
||||
|
||||
logger.debug("WebServiceSqlMapDAO insertReservation END");
|
||||
|
||||
getSqlSession().insert(sqlId, paramMap);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
40
src/main/java/com/madeuhome/dto/ReservationDto.java
Normal file
@@ -0,0 +1,40 @@
|
||||
package com.madeuhome.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 예약 관련 DTO
|
||||
*/
|
||||
@Data
|
||||
public class ReservationDto {
|
||||
// 예약 기본 정보
|
||||
private String muReserveId;
|
||||
private String userName;
|
||||
private String phoneNumber;
|
||||
private String birthDate;
|
||||
private String selectedDate;
|
||||
private String time;
|
||||
private String etc;
|
||||
|
||||
// 시술 정보
|
||||
private String categoryDivCd;
|
||||
private String categoryNo;
|
||||
private String postNo;
|
||||
private String procedureId;
|
||||
private String treatmentId;
|
||||
private String treatmentName;
|
||||
private String treatmentProcedureId;
|
||||
private String treatmentProcedureName;
|
||||
private String treatmentInfos;
|
||||
|
||||
// 병원 운영 정보
|
||||
private String muHospitalId;
|
||||
private String hospitalName;
|
||||
private String searchDate;
|
||||
private String openYn;
|
||||
private String holidayYn;
|
||||
private String openStartTime;
|
||||
private String openEndTime;
|
||||
private String breakStartTime;
|
||||
private String breakEndTime;
|
||||
}
|
||||
@@ -0,0 +1,211 @@
|
||||
package com.madeuhome.service.web.reservation;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.mybatis.spring.SqlSessionTemplate;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.madeuhome.constants.Constants;
|
||||
import com.madeuhome.util.OkHttpService;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* 예약 서비스 (구현체 포함)
|
||||
*/
|
||||
@Service
|
||||
@Slf4j
|
||||
public class ReservationService {
|
||||
|
||||
@Autowired
|
||||
private SqlSessionTemplate sqlSession;
|
||||
|
||||
@Value("${url.old-crm-php}")
|
||||
private String ocp;
|
||||
|
||||
/**
|
||||
* 예약 시술 정보 조회
|
||||
*/
|
||||
public HashMap<String, Object> selectReservation(HashMap<String, Object> paramMap) throws Exception {
|
||||
log.debug("ReservationService selectReservation START");
|
||||
HashMap<String, Object> map = new HashMap<>();
|
||||
|
||||
try {
|
||||
List<Map<String, Object>> listMap2 = new ArrayList<>();
|
||||
|
||||
if (paramMap.get("PROCEDURE_ID").toString().indexOf(",") != -1) {
|
||||
String[] PROCEDURE_ID = paramMap.get("PROCEDURE_ID").toString().split(",");
|
||||
for (int i = 0; i < PROCEDURE_ID.length; i++) {
|
||||
HashMap<String, Object> map2 = new HashMap<>();
|
||||
map2.put("PROCEDURE_ID", PROCEDURE_ID[i]);
|
||||
listMap2.add(sqlSession.selectOne("Reservation.selectReservationService", map2));
|
||||
}
|
||||
} else {
|
||||
listMap2.add(sqlSession.selectOne("Reservation.selectReservationService", paramMap));
|
||||
}
|
||||
map.put("reservation", listMap2);
|
||||
map.put("msgCode", Constants.OK);
|
||||
map.put("success", "true");
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
throw e;
|
||||
}
|
||||
|
||||
log.debug("ReservationService selectReservation END");
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* 병원 휴일 조회 (달력 표출용)
|
||||
*/
|
||||
public HashMap<String, Object> selectHospitalHolidayList(HashMap<String, Object> paramMap) throws Exception {
|
||||
log.debug("ReservationService selectHospitalHolidayList START");
|
||||
HashMap<String, Object> map = new HashMap<>();
|
||||
|
||||
try {
|
||||
List<Map<String, Object>> listMap = sqlSession.selectList("Reservation.selectHospitalHolidayList",
|
||||
paramMap);
|
||||
map.put("rows", listMap);
|
||||
map.put("msgCode", Constants.OK);
|
||||
map.put("success", "true");
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
throw e;
|
||||
}
|
||||
|
||||
log.debug("ReservationService selectHospitalHolidayList END");
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* 특정 날짜 운영시간 조회
|
||||
*/
|
||||
public HashMap<String, Object> selectHospitalWorkTime(HashMap<String, Object> paramMap) throws Exception {
|
||||
log.debug("ReservationService selectHospitalWorkTime START");
|
||||
HashMap<String, Object> map = new HashMap<>();
|
||||
|
||||
try {
|
||||
Map<String, Object> result = sqlSession.selectOne("Reservation.selectHospitalWorkTime", paramMap);
|
||||
if (result != null) {
|
||||
map.put("rows", result);
|
||||
} else {
|
||||
map.put("rows", new HashMap<>());
|
||||
}
|
||||
map.put("msgCode", Constants.OK);
|
||||
map.put("success", "true");
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
throw e;
|
||||
}
|
||||
|
||||
log.debug("ReservationService selectHospitalWorkTime END");
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* 예약 카운트 조회
|
||||
*/
|
||||
public HashMap<String, Object> selectReservationCnt(HashMap<String, Object> paramMap) throws Exception {
|
||||
log.debug("ReservationService selectReservationCnt START");
|
||||
HashMap<String, Object> map = new HashMap<>();
|
||||
|
||||
try {
|
||||
Map<String, Object> result = sqlSession.selectOne("Reservation.selectReservationCnt", paramMap);
|
||||
map.put("rows", result);
|
||||
map.put("msgCode", Constants.OK);
|
||||
map.put("success", "true");
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
throw e;
|
||||
}
|
||||
|
||||
log.debug("ReservationService selectReservationCnt END");
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* 예약 저장
|
||||
*/
|
||||
public HashMap<String, Object> insertReservation(HashMap<String, Object> paramMap) throws Exception {
|
||||
log.debug("ReservationService insertReservation START");
|
||||
HashMap<String, Object> map = new HashMap<>();
|
||||
try {
|
||||
String muReserveId = ("R").concat(String.valueOf(System.currentTimeMillis()));
|
||||
paramMap.put("muReserveId", muReserveId);
|
||||
ArrayList<String> userRequest = new ArrayList<>();
|
||||
|
||||
// TREATMENT_INFOS 파라미터가 있으면 파싱하여 P_변수명으로 세팅 후 insertReservation 호출
|
||||
if (paramMap.get("TREATMENT_INFOS") != null) {
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
List<Map<String, Object>> treatmentList = objectMapper.readValue(
|
||||
paramMap.get("TREATMENT_INFOS").toString(),
|
||||
new TypeReference<List<Map<String, Object>>>() {
|
||||
});
|
||||
for (Map<String, Object> t : treatmentList) {
|
||||
paramMap.put("P_TREATMENT_ID", t.get("MU_TREATMENT_ID"));
|
||||
paramMap.put("P_TREATMENT_NM", t.get("TREATMENT_NAME"));
|
||||
paramMap.put("P_PROCEDURE_ID", t.get("MU_TREATMENT_PROCEDURE_ID"));
|
||||
paramMap.put("P_PROCEDURE_NM", t.get("TREATMENT_PROCEDURE_NAME"));
|
||||
userRequest.add((String) t.get("TREATMENT_PROCEDURE_NAME"));
|
||||
sqlSession.insert("Reservation.insertReservation", paramMap);
|
||||
}
|
||||
} else {
|
||||
// 기존 방식대로 단일 시술 정보로 처리
|
||||
paramMap.put("P_TREATMENT_ID", paramMap.get("MU_TREATMENT_ID"));
|
||||
paramMap.put("P_TREATMENT_NM", paramMap.get("TREATMENT_NAME"));
|
||||
paramMap.put("P_PROCEDURE_ID", paramMap.get("MU_TREATMENT_PROCEDURE_ID"));
|
||||
paramMap.put("P_PROCEDURE_NM", paramMap.get("TREATMENT_PROCEDURE_NAME"));
|
||||
userRequest.add((String) paramMap.get("TREATMENT_PROCEDURE_NAME"));
|
||||
sqlSession.insert("Reservation.insertReservation", paramMap);
|
||||
}
|
||||
|
||||
Map<String, String> formData = new HashMap<>();
|
||||
formData.put("visitorName", (String) paramMap.get("NAME"));
|
||||
formData.put("visitorPhone", (String) paramMap.get("PHONE_NUMBER"));
|
||||
|
||||
String strUserRequest = "[선택시술]:" + userRequest.stream().collect(Collectors.joining(", "));
|
||||
String birthDate = "[생년월일]:" + paramMap.get("BIRTH_DATE");
|
||||
String reqTxt = "[요청사항]:" + (String) paramMap.get("ETC");
|
||||
formData.put("userRequest", strUserRequest + "\\n" + birthDate + "\\n" + reqTxt);
|
||||
String time = (String) paramMap.get("TIME");
|
||||
String date = (String) paramMap.get("SELECTED_DATE");
|
||||
String dateTime = date + " " + time;
|
||||
formData.put("datetime", dateTime);
|
||||
|
||||
Map<String, Object> detailMap = sqlSession.selectOne("Reservation.selectServiceDetail", paramMap);
|
||||
|
||||
formData.put("itemName", "[홈페이지] 다이어트센터");
|
||||
// 구CRM 연동ID가 없을 경우 기본값
|
||||
String oldCrmItemId = (String) detailMap.get("oldCrmItemId") == null
|
||||
? "NBI-2WlrXQdl-esws-5aA5-al7z-d7HwxL9tX0YF"
|
||||
: (String) detailMap.get("oldCrmItemId");
|
||||
formData.put("itemID", oldCrmItemId);
|
||||
formData.put("etcText", "[진료]:" + (String) detailMap.get("CATEGORY_NM"));
|
||||
|
||||
// OLD CRM으로 예약정보 POST
|
||||
OkHttpService.postFormData(ocp, formData);
|
||||
|
||||
map.put("msgCode", Constants.OK);
|
||||
map.put("success", "true");
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
throw e;
|
||||
}
|
||||
|
||||
log.debug("ReservationService insertReservation END");
|
||||
return map;
|
||||
}
|
||||
}
|
||||
@@ -36,7 +36,7 @@ public class WebInstagramServiceImpl implements WebInstagramService {
|
||||
HashMap<String, Object> paramMap) throws Exception {
|
||||
log.debug("WebInstagramServiceImpl selectListWebInstagram START");
|
||||
HashMap<String, Object> map = new HashMap<String, Object>();
|
||||
String apiUrl = "https://graph.instagram.com/" + clientId + "/media?fields=id,caption,media_url,permalink,media_type,thumbnail_url&access_token=" + accessToken;
|
||||
String apiUrl = "https://graph.facebook.com/" + clientId + "/media?fields=id,caption,media_url,permalink,media_type,thumbnail_url&access_token=" + accessToken;
|
||||
try{
|
||||
RestTemplate restTemplate = new RestTemplate();
|
||||
Map<String, Object> response = restTemplate.getForObject(apiUrl, Map.class);
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.madeuhome.service.web.webreview;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
public interface WebReviewService {
|
||||
public HashMap<String, Object> selectListProcedureReview(HashMap<String, Object> paramMap) throws Exception;
|
||||
|
||||
public HashMap<String, Object> selectProcedureReview(HashMap<String, Object> paramMap) throws Exception;
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
package com.madeuhome.service.web.webreview.impl;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import com.madeuhome.constants.Constants;
|
||||
import com.madeuhome.dao.web.webreview.WebReviewSqlMapDAO;
|
||||
import com.madeuhome.service.web.webreview.WebReviewService;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@Slf4j
|
||||
@Service("WebReviewService")
|
||||
public class WebReviewServiceImpl implements WebReviewService {
|
||||
|
||||
@Autowired
|
||||
private WebReviewSqlMapDAO webReviewSqlMapDAO;
|
||||
|
||||
/**
|
||||
* 시술후기 리스트 조회
|
||||
*/
|
||||
@Override
|
||||
public HashMap<String, Object> selectListProcedureReview(HashMap<String, Object> paramMap) throws Exception {
|
||||
log.debug("WebReviewServiceImpl selectListProcedureReview START");
|
||||
|
||||
HashMap<String, Object> map = new HashMap<String, Object>();
|
||||
|
||||
try {
|
||||
// 전체 건수 조회
|
||||
Map<String, Object> totalMap = webReviewSqlMapDAO.selectTotalProcedureReviewCount(paramMap);
|
||||
map.put("totalCount", totalMap.get("totalCount"));
|
||||
|
||||
// 리스트 조회
|
||||
List<Map<String, Object>> listMap = webReviewSqlMapDAO.selectListProcedureReview(paramMap);
|
||||
map.put("rows", listMap);
|
||||
|
||||
map.put("msgCode", Constants.OK);
|
||||
map.put("success", "true");
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
throw e;
|
||||
}
|
||||
|
||||
log.debug("WebReviewServiceImpl selectListProcedureReview END");
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* 시술후기 상세 조회
|
||||
*/
|
||||
@Override
|
||||
public HashMap<String, Object> selectProcedureReview(HashMap<String, Object> paramMap) throws Exception {
|
||||
log.debug("WebReviewServiceImpl selectProcedureReview START");
|
||||
|
||||
HashMap<String, Object> map = new HashMap<String, Object>();
|
||||
|
||||
try {
|
||||
// 조회수 증가
|
||||
webReviewSqlMapDAO.updateViewCount(paramMap);
|
||||
|
||||
// 상세 조회
|
||||
Map<String, Object> detailMap = webReviewSqlMapDAO.selectProcedureReview(paramMap);
|
||||
map.put("rows", detailMap);
|
||||
|
||||
map.put("msgCode", Constants.OK);
|
||||
map.put("success", "true");
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
throw e;
|
||||
}
|
||||
|
||||
log.debug("WebReviewServiceImpl selectProcedureReview END");
|
||||
return map;
|
||||
}
|
||||
}
|
||||
@@ -4,9 +4,9 @@ import java.util.HashMap;
|
||||
|
||||
public interface WebServiceService {
|
||||
public HashMap<String, Object> selectListWebCategory(HashMap<String, Object> paramMap) throws Exception;
|
||||
|
||||
public HashMap<String, Object> selectListWebService(HashMap<String, Object> paramMap) throws Exception;
|
||||
|
||||
public HashMap<String, Object> selectServiceDetail(HashMap<String, Object> paramMap) throws Exception;
|
||||
public HashMap<String, Object> selectReservation(HashMap<String, Object> paramMap) throws Exception;
|
||||
public HashMap<String, Object> selectReservationCnt(HashMap<String, Object> paramMap) throws Exception;
|
||||
public HashMap<String, Object> insertReservation(HashMap<String, Object> paramMap) throws Exception;
|
||||
|
||||
}
|
||||
|
||||
@@ -1,295 +1,114 @@
|
||||
package com.madeuhome.service.web.webservice.impl;
|
||||
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.madeuhome.constants.Constants;
|
||||
import com.madeuhome.dao.web.webservice.WebServiceSqlMapDAO;
|
||||
import com.madeuhome.service.web.webservice.WebServiceService;
|
||||
import com.madeuhome.util.OkHttpService;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@Slf4j
|
||||
@Service("WebServiceService")
|
||||
public class WebServiceServiceImpl implements WebServiceService{
|
||||
public class WebServiceServiceImpl implements WebServiceService {
|
||||
|
||||
@Autowired
|
||||
private WebServiceSqlMapDAO webServiceSqlMapDAO;
|
||||
|
||||
@Value("${url.old-crm-php}")
|
||||
private String ocp;
|
||||
|
||||
private WebServiceSqlMapDAO webServiceSqlMapDAO;
|
||||
|
||||
/**
|
||||
* 시술 정보 카테고리 리스트 조회 (List)
|
||||
*
|
||||
* @param paramMap
|
||||
* @return
|
||||
* @throws Exception
|
||||
* @throws Exception
|
||||
*/
|
||||
@Override
|
||||
public HashMap<String, Object> selectListWebCategory(HashMap<String, Object> paramMap) throws Exception {
|
||||
log.debug("WebServiceServiceImpl selectListWebCategory START");
|
||||
|
||||
|
||||
HashMap<String, Object> map = new HashMap<String, Object>();
|
||||
|
||||
try{
|
||||
try {
|
||||
List<Map<String, Object>> listMap = webServiceSqlMapDAO.selectListWebCategory(paramMap);
|
||||
map.put("rows",listMap);
|
||||
map.put("rows", listMap);
|
||||
|
||||
map.put("msgCode", Constants.OK);
|
||||
map.put("success","true");
|
||||
map.put("success", "true");
|
||||
|
||||
}catch (Exception e) {
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
throw e;
|
||||
throw e;
|
||||
}
|
||||
|
||||
|
||||
log.debug("WebServiceServiceImpl selectListWebCategory END");
|
||||
return map;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 시술 정보 리스트 조회 (List)
|
||||
*
|
||||
* @param paramMap
|
||||
* @return
|
||||
* @throws Exception
|
||||
* @throws Exception
|
||||
*/
|
||||
@Override
|
||||
public HashMap<String, Object> selectListWebService(HashMap<String, Object> paramMap) throws Exception {
|
||||
log.debug("WebServiceServiceImpl selectListWebService START");
|
||||
|
||||
|
||||
HashMap<String, Object> map = new HashMap<String, Object>();
|
||||
|
||||
try{
|
||||
try {
|
||||
List<Map<String, Object>> listMap = webServiceSqlMapDAO.selectListWebService(paramMap);
|
||||
|
||||
map.put("rows",listMap);
|
||||
|
||||
map.put("rows", listMap);
|
||||
|
||||
map.put("msgCode", Constants.OK);
|
||||
map.put("success","true");
|
||||
map.put("success", "true");
|
||||
|
||||
}catch (Exception e) {
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
throw e;
|
||||
throw e;
|
||||
}
|
||||
|
||||
|
||||
log.debug("WebServiceServiceImpl selectListWebService END");
|
||||
return map;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 시술 정보 상세 조회
|
||||
* 시술 정보 상세 조회
|
||||
*
|
||||
* @param paramMap
|
||||
* @return
|
||||
* @throws Exception
|
||||
* @throws Exception
|
||||
*/
|
||||
@Override
|
||||
public HashMap<String, Object> selectServiceDetail(HashMap<String, Object> paramMap) throws Exception {
|
||||
log.debug("WebServiceServiceImpl selectServiceDetail START");
|
||||
|
||||
|
||||
HashMap<String, Object> map = new HashMap<String, Object>();
|
||||
|
||||
try{
|
||||
//상세 내용
|
||||
try {
|
||||
// 상세 내용
|
||||
Map<String, Object> listMap = webServiceSqlMapDAO.selectServiceDetail(paramMap);
|
||||
map.put("rows",listMap);
|
||||
//시술 목록
|
||||
map.put("rows", listMap);
|
||||
// 시술 목록
|
||||
List<Map<String, Object>> listServiceMap = webServiceSqlMapDAO.selectListService(paramMap);
|
||||
map.put("price",listServiceMap);
|
||||
|
||||
map.put("msgCode", Constants.OK);
|
||||
map.put("success","true");
|
||||
map.put("price", listServiceMap);
|
||||
|
||||
}catch (Exception e) {
|
||||
map.put("msgCode", Constants.OK);
|
||||
map.put("success", "true");
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
throw e;
|
||||
throw e;
|
||||
}
|
||||
|
||||
|
||||
log.debug("WebServiceServiceImpl selectServiceDetail END");
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* 예약 정보 조회
|
||||
*
|
||||
* @param paramMap
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
@Override
|
||||
public HashMap<String, Object> selectReservation(HashMap<String, Object> paramMap) throws Exception {
|
||||
log.debug("WebServiceServiceImpl selectReservation START");
|
||||
HashMap<String, Object> map = new HashMap<String, Object>();
|
||||
|
||||
try{
|
||||
//예약 목록
|
||||
List<Map<String, Object>> listMap2 = new ArrayList<Map<String, Object>>();
|
||||
|
||||
if(paramMap.get("PROCEDURE_ID").toString().indexOf(",") != -1) {
|
||||
String[] PROCEDURE_ID = paramMap.get("PROCEDURE_ID").toString().split(",");
|
||||
|
||||
for(int i = 0; i < PROCEDURE_ID.length; i++) {
|
||||
HashMap<String, Object> map2 = new HashMap<String, Object>();
|
||||
map2.put("PROCEDURE_ID", PROCEDURE_ID[i]);
|
||||
listMap2.add(webServiceSqlMapDAO.selectReservationService(map2));
|
||||
}
|
||||
}else {
|
||||
listMap2.add(webServiceSqlMapDAO.selectReservationService(paramMap));
|
||||
}
|
||||
map.put("reservation",listMap2);
|
||||
map.put("msgCode", Constants.OK);
|
||||
map.put("success","true");
|
||||
|
||||
}catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
throw e;
|
||||
}
|
||||
|
||||
log.debug("WebServiceServiceImpl selectReservation END");
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* 예약 정보 조회
|
||||
*
|
||||
* @param paramMap
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
@Override
|
||||
public HashMap<String, Object> selectReservationCnt(HashMap<String, Object> paramMap) throws Exception {
|
||||
log.debug("WebServiceServiceImpl selectReservationCnt START");
|
||||
|
||||
HashMap<String, Object> map = new HashMap<String, Object>();
|
||||
|
||||
try{
|
||||
//상세 내용
|
||||
Map<String, Object> listMap = webServiceSqlMapDAO.selectReservationCnt(paramMap);
|
||||
map.put("rows",listMap);
|
||||
|
||||
map.put("msgCode", Constants.OK);
|
||||
map.put("success","true");
|
||||
|
||||
}catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
throw e;
|
||||
}
|
||||
|
||||
log.debug("WebServiceServiceImpl selectReservationCnt END");
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* 예약 정보 저장
|
||||
*
|
||||
* @param paramMap
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
@Override
|
||||
public HashMap<String, Object> insertReservation(HashMap<String, Object> paramMap) throws Exception {
|
||||
log.debug("WebServiceServiceImpl insertReservation START");
|
||||
HashMap<String, Object> map = new HashMap<String, Object>();
|
||||
try{
|
||||
String muReserveId = ("R").concat(String.valueOf(System.currentTimeMillis()));
|
||||
paramMap.put("muReserveId",muReserveId);
|
||||
ArrayList<String> userRequest = new ArrayList<String>();
|
||||
// TREATMENT_INFOS 파라미터가 있으면 파싱하여 P_변수명으로 세팅 후 insertReservation 호출
|
||||
if(paramMap.get("TREATMENT_INFOS") != null) {
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
List<Map<String, Object>> treatmentList = objectMapper.readValue(paramMap.get("TREATMENT_INFOS").toString(), new TypeReference<List<Map<String, Object>>>(){});
|
||||
for(Map<String, Object> t : treatmentList) {
|
||||
paramMap.put("P_TREATMENT_ID", t.get("MU_TREATMENT_ID"));
|
||||
paramMap.put("P_TREATMENT_NM", t.get("TREATMENT_NAME"));
|
||||
paramMap.put("P_PROCEDURE_ID", t.get("MU_TREATMENT_PROCEDURE_ID"));
|
||||
paramMap.put("P_PROCEDURE_NM", t.get("TREATMENT_PROCEDURE_NAME"));
|
||||
userRequest.add((String) t.get("TREATMENT_PROCEDURE_NAME"));
|
||||
webServiceSqlMapDAO.insertReservation(paramMap);
|
||||
}
|
||||
} else {
|
||||
// 기존 방식대로 단일 시술 정보로 처리
|
||||
paramMap.put("P_TREATMENT_ID", paramMap.get("MU_TREATMENT_ID"));
|
||||
paramMap.put("P_TREATMENT_NM", paramMap.get("TREATMENT_NAME"));
|
||||
paramMap.put("P_PROCEDURE_ID", paramMap.get("MU_TREATMENT_PROCEDURE_ID"));
|
||||
paramMap.put("P_PROCEDURE_NM", paramMap.get("TREATMENT_PROCEDURE_NAME"));
|
||||
userRequest.add((String) paramMap.get("TREATMENT_PROCEDURE_NAME"));
|
||||
webServiceSqlMapDAO.insertReservation(paramMap);
|
||||
}
|
||||
|
||||
Map<String, String> formData = new HashMap<>();
|
||||
formData.put("visitorName", (String) paramMap.get("NAME"));
|
||||
formData.put("visitorPhone", (String) paramMap.get("PHONE_NUMBER"));
|
||||
|
||||
String strUserRequest = "[선택시술]:" + userRequest.stream().collect(Collectors.joining(", "));
|
||||
String birthDate = "[생년월일]:" + paramMap.get("BIRTH_DATE");
|
||||
String reqTxt = "[요청사항]:" + (String) paramMap.get("ETC");
|
||||
formData.put("userRequest", strUserRequest + "\n" + birthDate + "\n" + reqTxt);
|
||||
String time = (String) paramMap.get("TIME");
|
||||
String date = (String) paramMap.get("SELECTED_DATE");
|
||||
String dateTime = date + " " + time;
|
||||
formData.put("datetime", dateTime);
|
||||
|
||||
Map<String, Object> detailMap = webServiceSqlMapDAO.selectServiceDetail(paramMap);
|
||||
|
||||
formData.put("itemName", "[홈페이지] 다이어트센터");
|
||||
// 구CRM 연동ID가 없을 경우 모두 (재방문) 다이어트 센터
|
||||
String oldCrmItemId = (String) detailMap.get("oldCrmItemId") == null ? "NBI-2WlrXQdl-esws-5aA5-al7z-d7HwxL9tX0YF" : (String) detailMap.get("oldCrmItemId");
|
||||
formData.put("itemID", oldCrmItemId);
|
||||
|
||||
formData.put("etcText", "[진료]:" + (String) detailMap.get("CATEGORY_NM"));
|
||||
|
||||
|
||||
// OLD CRM으로 예약정보 POST
|
||||
/**
|
||||
* //시술정보(상품명 , 상품ID)
|
||||
상품명 : 메쉬다주사(지방분해주사)
|
||||
상품 ID : NBI-1M0JEq5C-b8LM-4W0V-9YxZ-Z5r9XWJpIVEZ
|
||||
|
||||
피부 & 쁘띠센터
|
||||
NBI-4aJSJ67P-f0ID-4xds-aReQ-MnhfErLts0HP
|
||||
|
||||
프리미엄 레이저 리프팅
|
||||
NBI-1iPCWjih-2TDk-4NG3-ci56-tK7dgxxAT6dU
|
||||
|
||||
(첫방문) 다이어트 센터
|
||||
NBI-1Q8doiCm-qzTh-4imi-9lsK-6YDslUzIK2OO
|
||||
|
||||
(재방문) 다이어트 센터
|
||||
NBI-2WlrXQdl-esws-5aA5-al7z-d7HwxL9tX0YF
|
||||
|
||||
탈모약
|
||||
NBI-Nz00si0r-DkGc-5hdd-bs27-SLn0KHlHflS8
|
||||
|
||||
비만치료제 위고비
|
||||
NBI-2nJnTFVm-ieDy-4TNQ-czDK-Ll9FyixFr7zn
|
||||
**/
|
||||
OkHttpService.postFormData(ocp, formData);
|
||||
|
||||
|
||||
map.put("msgCode", Constants.OK);
|
||||
map.put("success","true");
|
||||
|
||||
}catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
throw e;
|
||||
}
|
||||
|
||||
System.out.println("[insertReservation] paramMap: " + paramMap);
|
||||
log.debug("WebServiceServiceImpl insertReservation END");
|
||||
return map;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ spring:
|
||||
password: madeu12#$
|
||||
|
||||
url:
|
||||
cdn: http://dcrm.vara.co.kr/cdn/
|
||||
cdn: https://newcrm.madeu.co.kr/cdn/
|
||||
old-crm-php: http://crm.madeu.co.kr/add-booking-home.php
|
||||
|
||||
madeu:
|
||||
|
||||
@@ -7,7 +7,7 @@ spring:
|
||||
password: apdlemdb12#$
|
||||
|
||||
url:
|
||||
cdn: http://crm.vara.co.kr/cdn/
|
||||
cdn: https://newcrm.madeu.co.kr/cdn/
|
||||
old-crm-php: http://crm.madeu.co.kr/add-booking-home.php
|
||||
|
||||
madeu:
|
||||
|
||||
@@ -7,7 +7,7 @@ spring:
|
||||
password: apdlemdb12#$
|
||||
|
||||
url:
|
||||
cdn: https://crm.vara.co.kr/cdn/
|
||||
cdn: https://newcrm.madeu.co.kr/cdn/
|
||||
old-crm-php: http://crm.madeu.co.kr/add-booking-home.php
|
||||
|
||||
madeu:
|
||||
|
||||
@@ -50,4 +50,4 @@ senderPhoneNumber: 025474711
|
||||
# Instagram 설정
|
||||
instagram:
|
||||
client-id: 17841468400622116
|
||||
accesstoken: IGAAMzYDUuoLJBZAE04bnBiaHFicnBybGJrTVVYQmVBa0p0WV9XNVRXLUpiV0taTFJFelBBQTZAoeXhxckpLelcwMVAyQ1VMdGhpUUhydWZAnYWpJSXFGeXozcGNMOFFmWGEtTXRxWHFDMUpJRkcycGoxT2o5MlppY09mRWhNVUVxOAZDZD
|
||||
accesstoken: EAAbtjekAF1EBQaZAZAmZCW8TyAA176J5yD6tFSj4MaZACgROAxGsvvT0cSvPxqLOHR4Bd8EeHSvWAqHZB8nHxurf3s3z0vtXt8sQ4zZBHpEJcnNTWSOvfch7hXv4uOQcfliBZCL6dDhMukAtgmXRHlfJasKDc5cZBcI7AuqNr1BZB0jUAwPC8uMUiIQ8dFjpRbnVyRwZDZD
|
||||
240
src/main/resources/mappers/ReservationMapper.xml
Normal file
@@ -0,0 +1,240 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="Reservation">
|
||||
|
||||
<!-- 병원 휴일 조회 (달력 표출용) -->
|
||||
<select id="selectHospitalHolidayList" parameterType="hashmap" resultType="hashmap">
|
||||
SELECT 'hospitalHoliday' AS "dateType"
|
||||
, HOSPITAL_HOLIDAY_NAME AS "hospitalHolidayName"
|
||||
, DATE_FORMAT(LOC_DATE, '%Y-%m-%d') AS "locDate"
|
||||
FROM MU_HOSPITAL_HOLIDAY MH
|
||||
WHERE MH.USE_YN = 'Y'
|
||||
AND MH.MU_HOSPITAL_ID = (SELECT MU_HOSPITAL_ID FROM MU_HOSPITAL WHERE USE_YN='Y' AND CENTER_DIV_CD='DIET' LIMIT 1)
|
||||
AND (
|
||||
(MH.REPEAT_YN = 'Y')
|
||||
OR
|
||||
(MH.REPEAT_YN = 'N' AND DATE_FORMAT( MH.LOC_DATE, '%Y' ) >= DATE_FORMAT( CURDATE( ), '%Y' ))
|
||||
)
|
||||
UNION ALL
|
||||
(
|
||||
SELECT 'publicHoliday' AS "dateType"
|
||||
, MPH.PUBLIC_HOLIDAY_NAME AS "publicHolidayName"
|
||||
, DATE_FORMAT(MPH.LOC_DATE, '%Y-%m-%d') AS "locDate"
|
||||
FROM MU_PUBLIC_HOLIDAY MPH
|
||||
JOIN MU_HOSPITAL MH
|
||||
ON MH.MU_HOSPITAL_ID = (SELECT MU_HOSPITAL_ID FROM MU_HOSPITAL WHERE USE_YN='Y' AND CENTER_DIV_CD='DIET' LIMIT 1)
|
||||
AND MH.PUBLIC_HOLIDAY_USE_YN = 'Y'
|
||||
WHERE MPH.USE_YN = 'Y'
|
||||
AND DATE_FORMAT( MPH.LOC_DATE, '%Y' ) >= DATE_FORMAT( CURDATE( ), '%Y' )
|
||||
)
|
||||
UNION ALL
|
||||
(
|
||||
SELECT CASE WHEN OPEN_YN = 'Y' THEN 'hospitalScheduleOpen' ELSE 'hospitalSchedule' END AS "dateType"
|
||||
, CASE WHEN OPEN_YN = 'Y' THEN '운영' ELSE '휴무' END AS "hospitalHolidayName"
|
||||
, DATE_FORMAT(SCHEDULE_DATE, '%Y-%m-%d') AS "locDate"
|
||||
FROM MU_HOSPITAL_SCHEDULE
|
||||
WHERE MU_HOSPITAL_ID = (SELECT MU_HOSPITAL_ID FROM MU_HOSPITAL WHERE USE_YN='Y' AND CENTER_DIV_CD='DIET' LIMIT 1)
|
||||
AND USE_YN = 'Y'
|
||||
AND DATE_FORMAT( SCHEDULE_DATE, '%Y' ) >= DATE_FORMAT( CURDATE( ), '%Y' )
|
||||
)
|
||||
</select>
|
||||
|
||||
<!-- 특정 날짜 운영시간 조회 -->
|
||||
<select id="selectHospitalWorkTime" parameterType="hashmap" resultType="hashmap">
|
||||
SELECT MH.MU_HOSPITAL_ID AS "muHospitalId"
|
||||
,MH.HOSPITAL_NAME AS "hospitalName"
|
||||
,CASE
|
||||
WHEN MHS.MU_HOSPITAL_SCHEDULE_ID IS NOT NULL THEN MHS.OPEN_YN
|
||||
ELSE
|
||||
CASE DAYOFWEEK(#{searchDate})
|
||||
WHEN 2 THEN MH.MON_OPEN_YN
|
||||
WHEN 3 THEN MH.TUE_OPEN_YN
|
||||
WHEN 4 THEN MH.WED_OPEN_YN
|
||||
WHEN 5 THEN MH.THU_OPEN_YN
|
||||
WHEN 6 THEN MH.FRI_OPEN_YN
|
||||
WHEN 7 THEN MH.SAT_OPEN_YN
|
||||
WHEN 1 THEN MH.SUN_OPEN_YN
|
||||
END
|
||||
END AS "openYn"
|
||||
,CASE
|
||||
WHEN MHS.MU_HOSPITAL_SCHEDULE_ID IS NOT NULL AND MHS.OPEN_YN = 'Y' THEN 'N'
|
||||
WHEN MHS.MU_HOSPITAL_SCHEDULE_ID IS NOT NULL AND MHS.OPEN_YN = 'N' THEN 'Y'
|
||||
WHEN EXISTS (
|
||||
SELECT 1
|
||||
FROM MU_HOSPITAL_HOLIDAY HH
|
||||
WHERE HH.USE_YN = 'Y'
|
||||
AND HH.MU_HOSPITAL_ID = MH.MU_HOSPITAL_ID
|
||||
AND (
|
||||
(HH.REPEAT_YN = 'Y' AND DATE_FORMAT(HH.LOC_DATE, '%m-%d') = DATE_FORMAT(#{searchDate}, '%m-%d'))
|
||||
OR
|
||||
(HH.REPEAT_YN = 'N' AND HH.LOC_DATE = #{searchDate})
|
||||
)) OR
|
||||
EXISTS (
|
||||
SELECT 1
|
||||
FROM MU_PUBLIC_HOLIDAY MPH
|
||||
WHERE MPH.USE_YN = 'Y'
|
||||
AND MH.PUBLIC_HOLIDAY_USE_YN='Y'
|
||||
AND MPH.LOC_DATE = #{searchDate}
|
||||
)
|
||||
THEN 'Y'
|
||||
ELSE 'N'
|
||||
END AS "holidayYn"
|
||||
,CASE
|
||||
WHEN MHS.MU_HOSPITAL_SCHEDULE_ID IS NOT NULL THEN MHS.OPEN_START_TIME
|
||||
ELSE
|
||||
CASE DAYOFWEEK(#{searchDate})
|
||||
WHEN 2 THEN MH.MON_OPEN_START_TIME
|
||||
WHEN 3 THEN MH.TUE_OPEN_START_TIME
|
||||
WHEN 4 THEN MH.WED_OPEN_START_TIME
|
||||
WHEN 5 THEN MH.THU_OPEN_START_TIME
|
||||
WHEN 6 THEN MH.FRI_OPEN_START_TIME
|
||||
WHEN 7 THEN MH.SAT_OPEN_START_TIME
|
||||
WHEN 1 THEN MH.SUN_OPEN_START_TIME
|
||||
END
|
||||
END AS "openStartTime"
|
||||
,CASE
|
||||
WHEN MHS.MU_HOSPITAL_SCHEDULE_ID IS NOT NULL THEN MHS.OPEN_END_TIME
|
||||
ELSE
|
||||
CASE DAYOFWEEK(#{searchDate})
|
||||
WHEN 2 THEN MH.MON_OPEN_END_TIME
|
||||
WHEN 3 THEN MH.TUE_OPEN_END_TIME
|
||||
WHEN 4 THEN MH.WED_OPEN_END_TIME
|
||||
WHEN 5 THEN MH.THU_OPEN_END_TIME
|
||||
WHEN 6 THEN MH.FRI_OPEN_END_TIME
|
||||
WHEN 7 THEN MH.SAT_OPEN_END_TIME
|
||||
WHEN 1 THEN MH.SUN_OPEN_END_TIME
|
||||
END
|
||||
END AS "openEndTime"
|
||||
,CASE
|
||||
WHEN MHS.MU_HOSPITAL_SCHEDULE_ID IS NOT NULL THEN MHS.BREAK_START_TIME
|
||||
ELSE
|
||||
CASE DAYOFWEEK(#{searchDate})
|
||||
WHEN 2 THEN MH.MON_BREAK_START_TIME
|
||||
WHEN 3 THEN MH.TUE_BREAK_START_TIME
|
||||
WHEN 4 THEN MH.WED_BREAK_START_TIME
|
||||
WHEN 5 THEN MH.THU_BREAK_START_TIME
|
||||
WHEN 6 THEN MH.FRI_BREAK_START_TIME
|
||||
WHEN 7 THEN MH.SAT_BREAK_START_TIME
|
||||
WHEN 1 THEN MH.SUN_BREAK_START_TIME
|
||||
END
|
||||
END AS "breakStartTime"
|
||||
,CASE
|
||||
WHEN MHS.MU_HOSPITAL_SCHEDULE_ID IS NOT NULL THEN MHS.BREAK_END_TIME
|
||||
ELSE
|
||||
CASE DAYOFWEEK(#{searchDate})
|
||||
WHEN 2 THEN MH.MON_BREAK_END_TIME
|
||||
WHEN 3 THEN MH.TUE_BREAK_END_TIME
|
||||
WHEN 4 THEN MH.WED_BREAK_END_TIME
|
||||
WHEN 5 THEN MH.THU_BREAK_END_TIME
|
||||
WHEN 6 THEN MH.FRI_BREAK_END_TIME
|
||||
WHEN 7 THEN MH.SAT_BREAK_END_TIME
|
||||
WHEN 1 THEN MH.SUN_BREAK_END_TIME
|
||||
END
|
||||
END AS "breakEndTime"
|
||||
,MH.PUBLIC_HOLIDAY_USE_YN AS "publicHolidayUseYn"
|
||||
FROM MU_HOSPITAL AS MH
|
||||
LEFT JOIN MU_HOSPITAL_SCHEDULE MHS
|
||||
ON MH.MU_HOSPITAL_ID = MHS.MU_HOSPITAL_ID
|
||||
AND MHS.SCHEDULE_DATE = #{searchDate}
|
||||
AND MHS.USE_YN = 'Y'
|
||||
WHERE MH.USE_YN = 'Y'
|
||||
AND MH.CENTER_DIV_CD = 'DIET'
|
||||
LIMIT 0, 1
|
||||
</select>
|
||||
|
||||
<!-- 예약 카운트 조회 -->
|
||||
<select id="selectReservationCnt" resultType="hashmap" parameterType="hashmap">
|
||||
SELECT COUNT(*) AS RES_CNT
|
||||
FROM MU_RESERVE
|
||||
WHERE RESERVE_DATE = #{SELECTED_DATE}
|
||||
AND RESERVE_TIME = #{TIME}
|
||||
</select>
|
||||
|
||||
<!-- 예약 시술 정보 조회 -->
|
||||
<select id="selectReservationService" resultType="hashmap" parameterType="hashmap">
|
||||
SELECT A.TREATMENT_PROCEDURE_NAME, (B.PRICE + B.VAT) AS PRICE, B.VAT, B.DISCOUNT_PRICE, C.MU_TREATMENT_ID, C.TREATMENT_NAME, B.MU_TREATMENT_PROCEDURE_ID
|
||||
FROM MU_TREATMENT_PROCEDURE A
|
||||
LEFT OUTER JOIN MU_TREATMENT_PROCEDURE_PRICE B ON A.MU_TREATMENT_PROCEDURE_ID = B.MU_TREATMENT_PROCEDURE_ID AND B.USE_YN = 'Y'
|
||||
LEFT OUTER JOIN MU_TREATMENT C ON A.MU_TREATMENT_ID = C.MU_TREATMENT_ID
|
||||
WHERE A.MU_TREATMENT_PROCEDURE_ID = #{PROCEDURE_ID}
|
||||
</select>
|
||||
|
||||
<!-- 시술 상세 조회 (CRM 연동용) -->
|
||||
<select id="selectServiceDetail" resultType="hashmap" parameterType="hashmap">
|
||||
SELECT A.CATEGORY_DIV_CD
|
||||
, A.CATEGORY_NO
|
||||
, A.POST_NO
|
||||
, C.CATEGORY_NM
|
||||
, A.TITLE
|
||||
, A.CONTENT
|
||||
, A.THUMBNAIL_BOTTOM_TXT
|
||||
, A.HASHTAG
|
||||
, A.OLD_CRM_ITEM_ID as oldCrmItemId
|
||||
, D.FILE_PATH AS THUMBNAIL_PATH
|
||||
, E.FILE_PATH AS CONTENTS_PATH
|
||||
, (SELECT MIN(PRICE + VAT) FROM MU_TREATMENT_PROCEDURE_PRICE C
|
||||
LEFT OUTER JOIN HP_CONTENTS_BBS_PROCEDURE B ON A.POST_NO = B.POST_NO AND A.CATEGORY_DIV_CD = B.CATEGORY_DIV_CD AND A.CATEGORY_NO = B.CATEGORY_NO AND B.USE_YN = 'Y'
|
||||
WHERE B.MU_TREATMENT_PROCEDURE_ID = C.MU_TREATMENT_PROCEDURE_ID AND C.USE_YN = 'Y') AS PRICE
|
||||
, (SELECT MIN(DISCOUNT_PRICE) FROM MU_TREATMENT_PROCEDURE_PRICE C
|
||||
LEFT OUTER JOIN HP_CONTENTS_BBS_PROCEDURE B ON A.POST_NO = B.POST_NO AND A.CATEGORY_DIV_CD = B.CATEGORY_DIV_CD AND A.CATEGORY_NO = B.CATEGORY_NO AND B.USE_YN = 'Y'
|
||||
WHERE B.MU_TREATMENT_PROCEDURE_ID = C.MU_TREATMENT_PROCEDURE_ID AND C.USE_YN = 'Y') AS DISCOUNT_PRICE
|
||||
FROM HP_CONTENTS_BBS A
|
||||
LEFT OUTER JOIN HP_CATEGORY C ON A.CATEGORY_NO = C.CATEGORY_NO AND A.CATEGORY_DIV_CD = C.CATEGORY_DIV_CD
|
||||
LEFT OUTER JOIN HP_ATTACH_FILE D ON A.THUMBNAIL_ATTACHFILE_ID = D.ATTACHFILE_ID
|
||||
LEFT OUTER JOIN HP_ATTACH_FILE E ON A.CONTENTS_ATTACHFILE_ID = E.ATTACHFILE_ID
|
||||
WHERE A.USE_YN = 'Y'
|
||||
AND A.CATEGORY_DIV_CD = #{CATEGORY_DIV_CD}
|
||||
AND A.CATEGORY_NO = #{CATEGORY_NO}
|
||||
AND A.POST_NO = #{POST_NO}
|
||||
</select>
|
||||
|
||||
<!-- 예약 저장 -->
|
||||
<insert id="insertReservation" parameterType="hashmap">
|
||||
<selectKey resultType="string" keyProperty="id" order="BEFORE">
|
||||
SELECT CONCAT(#{muReserveId},LPAD((SELECT NEXTVAL(MU_RESERVE_SEQ)), 11, 0))
|
||||
</selectKey>
|
||||
INSERT INTO MU_RESERVE(
|
||||
MU_RESERVE_ID
|
||||
,USER_NAME
|
||||
,PHONE_NUMBER
|
||||
,RESERVE_DATE
|
||||
,RESERVE_TIME
|
||||
,MU_TREATMENT_ID
|
||||
,TREATMENT_NAME
|
||||
,MU_TREATMENT_PROCEDURE_ID
|
||||
,TREATMENT_PROCEDURE_NAME
|
||||
,ETC
|
||||
,STATUS
|
||||
,STATUS2
|
||||
,WRITE_DATE
|
||||
,WRITE_TIME
|
||||
,CUD_FLAG
|
||||
,USE_YN
|
||||
,REG_ID
|
||||
,REG_DATE
|
||||
,MOD_ID
|
||||
,MOD_DATE
|
||||
)VALUES(
|
||||
#{id}
|
||||
,#{NAME}
|
||||
,#{PHONE_NUMBER}
|
||||
,#{SELECTED_DATE}
|
||||
,#{TIME}
|
||||
,#{P_TREATMENT_ID}
|
||||
,#{P_TREATMENT_NM}
|
||||
,#{P_PROCEDURE_ID}
|
||||
,#{P_PROCEDURE_NM}
|
||||
,#{ETC,jdbcType=VARCHAR}
|
||||
,'T'
|
||||
,'N'
|
||||
,CURDATE()
|
||||
,CURTIME()
|
||||
,'C'
|
||||
,'Y'
|
||||
,'customer'
|
||||
,NOW()
|
||||
,'customer'
|
||||
,NOW()
|
||||
)
|
||||
</insert>
|
||||
</mapper>
|
||||
@@ -15,6 +15,8 @@
|
||||
<!-- 카테고리 상세 목록 조회 -->
|
||||
<select id="selectListEvent" parameterType="hashmap" resultType="hashmap">
|
||||
SELECT A.CATEGORY_DIV_CD, A.CATEGORY_NO, A.POST_NO, TITLE, CONTENT, THUMBNAIL_BOTTOM_TXT, A.HASHTAG, D.FILE_PATH AS THUMBNAIL_PATH,
|
||||
DATE_FORMAT(A.EVENT_START_DT, '%Y-%m-%d') AS EVENT_START_DT,
|
||||
DATE_FORMAT(A.EVENT_END_DT, '%Y-%m-%d') AS EVENT_END_DT,
|
||||
(SELECT MIN(PRICE + VAT) FROM MU_TREATMENT_PROCEDURE_PRICE C
|
||||
LEFT OUTER JOIN HP_CONTENTS_BBS_PROCEDURE B ON A.POST_NO = B.POST_NO AND A.CATEGORY_DIV_CD = B.CATEGORY_DIV_CD AND A.CATEGORY_NO = B.CATEGORY_NO AND B.USE_YN = 'Y'
|
||||
WHERE B.MU_TREATMENT_PROCEDURE_ID = C.MU_TREATMENT_PROCEDURE_ID AND C.USE_YN = 'Y') AS PRICE,
|
||||
@@ -26,12 +28,14 @@
|
||||
WHERE A.USE_YN = 'Y'
|
||||
AND A.CATEGORY_DIV_CD = '02'
|
||||
AND A.CATEGORY_NO = #{category_no}
|
||||
ORDER BY A.REG_DATE DESC
|
||||
ORDER BY ORD_NO DESC
|
||||
</select>
|
||||
|
||||
<!-- 이벤트안내 상세 조회 -->
|
||||
<select id="selectEventDetail" resultType="hashmap" parameterType="hashmap">
|
||||
SELECT A.CATEGORY_DIV_CD, A.CATEGORY_NO, A.POST_NO, C.CATEGORY_NM , TITLE, CONTENT, THUMBNAIL_BOTTOM_TXT, A.HASHTAG, D.FILE_PATH AS THUMBNAIL_PATH, E.FILE_PATH AS CONTENTS_PATH,
|
||||
DATE_FORMAT(A.EVENT_START_DT, '%Y-%m-%d') AS EVENT_START_DT,
|
||||
DATE_FORMAT(A.EVENT_END_DT, '%Y-%m-%d') AS EVENT_END_DT,
|
||||
(SELECT MIN(PRICE + VAT) FROM MU_TREATMENT_PROCEDURE_PRICE C
|
||||
LEFT OUTER JOIN HP_CONTENTS_BBS_PROCEDURE B ON A.POST_NO = B.POST_NO AND A.CATEGORY_DIV_CD = B.CATEGORY_DIV_CD AND A.CATEGORY_NO = B.CATEGORY_NO AND B.USE_YN = 'Y'
|
||||
WHERE B.MU_TREATMENT_PROCEDURE_ID = C.MU_TREATMENT_PROCEDURE_ID AND C.USE_YN = 'Y') AS PRICE,
|
||||
|
||||
68
src/main/resources/mappers/WebReviewSqlMap.xml
Normal file
@@ -0,0 +1,68 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="WebReview">
|
||||
|
||||
<!-- 시술후기 전체 건수 조회 -->
|
||||
<select id="selectTotalProcedureReviewCount" parameterType="hashmap" resultType="hashmap">
|
||||
SELECT COUNT(*) AS "totalCount"
|
||||
FROM MU_PROCEDURE_REVIEW MPR
|
||||
WHERE MPR.USE_YN = 'Y'
|
||||
AND MPR.CATEGORY_DIV_CD = #{categoryDivCd}
|
||||
<if test="title != null and title != ''">
|
||||
AND MPR.TITLE LIKE CONCAT('%', TRIM(#{title}), '%')
|
||||
</if>
|
||||
</select>
|
||||
|
||||
<!-- 시술후기 리스트 조회 -->
|
||||
<select id="selectListProcedureReview" parameterType="hashmap" resultType="hashmap">
|
||||
SELECT MPR.*
|
||||
FROM (
|
||||
SELECT MPR.*
|
||||
,CAST(@RNUM:=@RNUM + 1 AS CHAR) AS "rowNum"
|
||||
FROM (
|
||||
SELECT MPR.MU_PROCEDURE_REVIEW_ID AS "muProcedureReviewId"
|
||||
,MPR.CATEGORY_DIV_CD AS "categoryDivCd"
|
||||
,MPR.TITLE AS "title"
|
||||
,MPR.CONTENT AS "summary"
|
||||
,MPR.HASHTAG AS "hashtag"
|
||||
,MPR.VIEW_COUNT AS "viewCount"
|
||||
,DATE_FORMAT(MPR.REG_DATE, '%Y-%m-%d') AS "writeDate"
|
||||
FROM MU_PROCEDURE_REVIEW MPR
|
||||
WHERE MPR.USE_YN = 'Y'
|
||||
AND MPR.CATEGORY_DIV_CD = #{categoryDivCd}
|
||||
<if test="title != null and title != ''">
|
||||
AND MPR.TITLE LIKE CONCAT('%', TRIM(#{title}), '%')
|
||||
</if>
|
||||
ORDER BY MPR.REG_DATE DESC
|
||||
LIMIT 18446744073709551615
|
||||
) MPR, (SELECT @RNUM:=0) R
|
||||
WHERE 1 = 1
|
||||
) MPR
|
||||
WHERE 1 = 1
|
||||
LIMIT ${start}, ${limit}
|
||||
</select>
|
||||
|
||||
<!-- 시술후기 상세 조회 -->
|
||||
<select id="selectProcedureReview" parameterType="hashmap" resultType="hashmap">
|
||||
SELECT MPR.MU_PROCEDURE_REVIEW_ID AS "muProcedureReviewId"
|
||||
,MPR.CATEGORY_DIV_CD AS "categoryDivCd"
|
||||
,MPR.TITLE AS "title"
|
||||
,MPR.CONTENT AS "content"
|
||||
,MPR.HASHTAG AS "hashtag"
|
||||
,MPR.VIEW_COUNT AS "viewCount"
|
||||
,DATE_FORMAT(MPR.REG_DATE, '%Y-%m-%d') AS "writeDate"
|
||||
FROM MU_PROCEDURE_REVIEW MPR
|
||||
WHERE MPR.USE_YN = 'Y'
|
||||
AND MPR.MU_PROCEDURE_REVIEW_ID = #{muProcedureReviewId}
|
||||
LIMIT 0, 1
|
||||
</select>
|
||||
|
||||
<!-- 조회수 증가 -->
|
||||
<update id="updateViewCount" parameterType="hashmap">
|
||||
UPDATE MU_PROCEDURE_REVIEW
|
||||
SET VIEW_COUNT = IFNULL(VIEW_COUNT, 0) + 1
|
||||
WHERE USE_YN = 'Y'
|
||||
AND MU_PROCEDURE_REVIEW_ID = #{muProcedureReviewId}
|
||||
</update>
|
||||
|
||||
</mapper>
|
||||
@@ -25,6 +25,7 @@
|
||||
WHERE A.USE_YN = 'Y'
|
||||
AND A.CATEGORY_DIV_CD = '01'
|
||||
AND A.CATEGORY_NO = #{categoryNo}
|
||||
ORDER BY ORD_NO DESC
|
||||
</select>
|
||||
|
||||
<!-- 카테고리 상세 조회 -->
|
||||
@@ -68,71 +69,5 @@
|
||||
AND A.CATEGORY_NO = #{CATEGORY_NO}
|
||||
AND A.POST_NO = #{POST_NO}
|
||||
</select>
|
||||
|
||||
<!-- 예약 조회 -->
|
||||
<select id="selectReservationCnt" resultType="hashmap" parameterType="hashmap">
|
||||
SELECT COUNT(*) AS RES_CNT
|
||||
FROM MU_RESERVE
|
||||
WHERE RESERVE_DATE = #{SELECTED_DATE}
|
||||
AND RESERVE_TIME = #{TIME}
|
||||
</select>
|
||||
|
||||
<!-- 시술 목록 조회 -->
|
||||
<select id="selectReservationService" resultType="hashmap" parameterType="hashmap">
|
||||
SELECT A.TREATMENT_PROCEDURE_NAME, (B.PRICE + B.VAT) AS PRICE, B.VAT, B.DISCOUNT_PRICE, C.MU_TREATMENT_ID, C.TREATMENT_NAME, B.MU_TREATMENT_PROCEDURE_ID
|
||||
FROM MU_TREATMENT_PROCEDURE A
|
||||
LEFT OUTER JOIN MU_TREATMENT_PROCEDURE_PRICE B ON A.MU_TREATMENT_PROCEDURE_ID = B.MU_TREATMENT_PROCEDURE_ID AND B.USE_YN = 'Y'
|
||||
LEFT OUTER JOIN MU_TREATMENT C ON A.MU_TREATMENT_ID = C.MU_TREATMENT_ID
|
||||
WHERE A.MU_TREATMENT_PROCEDURE_ID = #{PROCEDURE_ID}
|
||||
</select>
|
||||
|
||||
<!-- 예약 저장 -->
|
||||
<insert id="insertReservation" parameterType="hashmap">
|
||||
<selectKey resultType="string" keyProperty="id" order="BEFORE">
|
||||
SELECT CONCAT(#{muReserveId},LPAD((SELECT NEXTVAL(MU_RESERVE_SEQ)), 11, 0))
|
||||
</selectKey>
|
||||
INSERT INTO MU_RESERVE(
|
||||
MU_RESERVE_ID
|
||||
,USER_NAME
|
||||
,PHONE_NUMBER
|
||||
,RESERVE_DATE
|
||||
,RESERVE_TIME
|
||||
,MU_TREATMENT_ID
|
||||
,TREATMENT_NAME
|
||||
,MU_TREATMENT_PROCEDURE_ID
|
||||
,TREATMENT_PROCEDURE_NAME
|
||||
,ETC
|
||||
,STATUS
|
||||
,STATUS2
|
||||
,WRITE_DATE
|
||||
,WRITE_TIME
|
||||
,CUD_FLAG
|
||||
,USE_YN
|
||||
,REG_ID
|
||||
,REG_DATE
|
||||
,MOD_ID
|
||||
,MOD_DATE
|
||||
)VALUES(
|
||||
#{id}
|
||||
,#{NAME}
|
||||
,#{PHONE_NUMBER}
|
||||
,#{SELECTED_DATE}
|
||||
,#{TIME}
|
||||
,#{P_TREATMENT_ID}
|
||||
,#{P_TREATMENT_NM}
|
||||
,#{P_PROCEDURE_ID}
|
||||
,#{P_PROCEDURE_NM}
|
||||
,#{ETC,jdbcType=VARCHAR}
|
||||
,'T'
|
||||
,'N'
|
||||
,CURDATE()
|
||||
,CURTIME()
|
||||
,'C'
|
||||
,'Y'
|
||||
,'customer'
|
||||
,NOW()
|
||||
,'customer'
|
||||
,NOW()
|
||||
)
|
||||
</insert>
|
||||
</mapper>
|
||||
@@ -132,6 +132,10 @@
|
||||
color: #333;
|
||||
border-radius: 0;
|
||||
height: 100%;
|
||||
line-height:16px;
|
||||
word-break: keep-all;
|
||||
overflow: hidden;
|
||||
text-overflow: clip;
|
||||
}
|
||||
.popup .nav.nav-tabs li.active a,
|
||||
.popup .nav.nav-tabs li[role="presentation"].active a {
|
||||
|
||||
493
src/main/resources/static/css/web/makeReservation.css
Normal file
@@ -0,0 +1,493 @@
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Noto Sans KR', sans-serif;
|
||||
margin: 0;
|
||||
background: #fafafa;
|
||||
color: #222;
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
/* Main container */
|
||||
.reservation-container {
|
||||
display: flex;
|
||||
max-width: 1280px !important;
|
||||
margin: 0 auto;
|
||||
gap: 20px;
|
||||
height: 100%;
|
||||
flex: 1;
|
||||
padding: 20px 0;
|
||||
margin-top: 65px;
|
||||
}
|
||||
|
||||
.box {
|
||||
background: #fff;
|
||||
border-radius: 16px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
|
||||
padding: 24px 16px;
|
||||
flex: 1 1 0;
|
||||
min-width: 260px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: fit-content;
|
||||
min-height: 600px;
|
||||
}
|
||||
|
||||
.step-title {
|
||||
color: #b23c3c;
|
||||
font-weight: 700;
|
||||
font-size: 1.1em;
|
||||
margin-bottom: 12px;
|
||||
letter-spacing: 0.02em;
|
||||
transition: color 0.3s ease;
|
||||
}
|
||||
|
||||
.step-title.completed {
|
||||
color: #008000 !important;
|
||||
}
|
||||
|
||||
/* Service section */
|
||||
.service-list {
|
||||
flex: 1;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.service-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
font-size: 1.1em;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.service-item .del {
|
||||
cursor: pointer;
|
||||
color: #b23c3c;
|
||||
margin-left: 8px;
|
||||
font-size: 1.2em;
|
||||
transition: color 0.3s ease, opacity 0.3s ease;
|
||||
}
|
||||
|
||||
.service-item .del:hover {
|
||||
color: #ff0000;
|
||||
}
|
||||
|
||||
.service-item .del.disabled {
|
||||
color: #ccc;
|
||||
cursor: not-allowed;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.service-item .del.disabled:hover {
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
.service-item .price {
|
||||
font-weight: bold;
|
||||
color: #b23c3c;
|
||||
}
|
||||
|
||||
/* Service count indicator */
|
||||
.service-count-info {
|
||||
font-size: 0.85em;
|
||||
color: #888;
|
||||
margin-bottom: 8px;
|
||||
text-align: center;
|
||||
padding: 4px 8px;
|
||||
background: #f8f9fa;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.service-count-info.single {
|
||||
color: #ff8c00;
|
||||
background: #fff3e0;
|
||||
border: 1px solid #ffcc80;
|
||||
}
|
||||
|
||||
.total {
|
||||
border-top: 1px solid #eee;
|
||||
margin-top: auto;
|
||||
padding-top: 16px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
font-size: 1.1em;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.total .price {
|
||||
color: #b23c3c;
|
||||
}
|
||||
|
||||
.total small {
|
||||
font-weight: normal;
|
||||
color: #888;
|
||||
font-size: 0.9em;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
/* Calendar section */
|
||||
.calendar-box {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.calendar-header {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.calendar-header button {
|
||||
background: none;
|
||||
border: none;
|
||||
font-size: 1.2em;
|
||||
cursor: pointer;
|
||||
color: #b23c3c;
|
||||
padding: 0 6px;
|
||||
}
|
||||
|
||||
.calendar-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
text-align: center;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.calendar-table th,
|
||||
.calendar-table td {
|
||||
width: 2em;
|
||||
height: 2em;
|
||||
padding: 2px;
|
||||
font-size: 1em;
|
||||
border-radius: 50%;
|
||||
cursor: pointer;
|
||||
transition: background 0.15s;
|
||||
}
|
||||
|
||||
.calendar-table th {
|
||||
color: #b23c3c;
|
||||
font-weight: 500;
|
||||
background: none;
|
||||
}
|
||||
|
||||
.calendar-table td.selected {
|
||||
background: #b23c3c;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.calendar-table td.today {
|
||||
border: 1.5px solid #b23c3c;
|
||||
}
|
||||
|
||||
.calendar-table td:not(.selected):hover {
|
||||
background: #f5eaea;
|
||||
}
|
||||
|
||||
.calendar-table td.disabled {
|
||||
color: #ccc;
|
||||
pointer-events: none;
|
||||
background: none;
|
||||
}
|
||||
|
||||
/* Time slots */
|
||||
.time-slots {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.time-btn {
|
||||
flex: 1 0 30%;
|
||||
min-width: 80px;
|
||||
padding: 8px 0;
|
||||
border: 1px solid #b23c3c;
|
||||
border-radius: 20px;
|
||||
background: #fff;
|
||||
color: #b23c3c;
|
||||
font-size: 1em;
|
||||
cursor: pointer;
|
||||
transition: background 0.15s, color 0.15s;
|
||||
}
|
||||
|
||||
.time-btn.selected,
|
||||
.time-btn:active {
|
||||
background: #b23c3c;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.time-btn:disabled {
|
||||
color: #ccc;
|
||||
border-color: #eee;
|
||||
background: #f5f5f5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.person-count {
|
||||
margin-top: 12px;
|
||||
font-size: 0.98em;
|
||||
color: #888;
|
||||
}
|
||||
|
||||
/* Form section */
|
||||
.form-group {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.form-group label {
|
||||
display: block;
|
||||
margin-bottom: 4px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.form-group input,
|
||||
.form-group textarea {
|
||||
width: 100%;
|
||||
padding: 8px 10px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 6px;
|
||||
font-size: 1em;
|
||||
resize: none;
|
||||
transition: border-color 0.3s ease;
|
||||
}
|
||||
|
||||
.form-group textarea {
|
||||
min-height: 60px;
|
||||
}
|
||||
|
||||
.checkbox-group {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 18px;
|
||||
font-size: 0.98em;
|
||||
}
|
||||
|
||||
.checkbox-group input[type="checkbox"] {
|
||||
margin-right: 8px;
|
||||
accent-color: #b23c3c;
|
||||
}
|
||||
|
||||
.submit-btn {
|
||||
width: 100%;
|
||||
padding: 14px 0;
|
||||
background: #ddd;
|
||||
color: #888;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
font-size: 1.1em;
|
||||
font-weight: bold;
|
||||
cursor: not-allowed;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.submit-btn.step-progress {
|
||||
background: linear-gradient(45deg, #ddd, #bbb);
|
||||
color: #666;
|
||||
font-size: 0.95em;
|
||||
}
|
||||
|
||||
.submit-btn.ready {
|
||||
background: #b23c3c;
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
animation: pulse 2s infinite;
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0% { transform: scale(1); }
|
||||
50% { transform: scale(1.02); }
|
||||
100% { transform: scale(1); }
|
||||
}
|
||||
|
||||
/* Phone message styles (common.js PhoneValidator용) */
|
||||
.phone-message {
|
||||
font-size: 12px;
|
||||
margin-top: 5px;
|
||||
padding: 4px 8px;
|
||||
border-radius: 4px;
|
||||
font-weight: 500;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.phone-message.error {
|
||||
color: #ff0000;
|
||||
background-color: #ffebee;
|
||||
border: 1px solid #ffcdd2;
|
||||
}
|
||||
|
||||
.phone-message.warning {
|
||||
color: #ff8c00;
|
||||
background-color: #fff3e0;
|
||||
border: 1px solid #ffcc80;
|
||||
}
|
||||
|
||||
.phone-message.success {
|
||||
color: #008000;
|
||||
background-color: #f1f8e9;
|
||||
border: 1px solid #c8e6c9;
|
||||
}
|
||||
|
||||
.phone-message.info {
|
||||
color: #2196f3;
|
||||
background-color: #e3f2fd;
|
||||
border: 1px solid #90caf9;
|
||||
}
|
||||
|
||||
/* Birth date message styles */
|
||||
.birth-date-message {
|
||||
font-size: 12px;
|
||||
margin-top: 5px;
|
||||
padding: 4px 8px;
|
||||
border-radius: 4px;
|
||||
font-weight: 500;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.birth-date-message.error {
|
||||
color: #ff0000;
|
||||
background-color: #ffebee;
|
||||
border: 1px solid #ffcdd2;
|
||||
}
|
||||
|
||||
.birth-date-message.warning {
|
||||
color: #ff8c00;
|
||||
background-color: #fff3e0;
|
||||
border: 1px solid #ffcc80;
|
||||
}
|
||||
|
||||
.birth-date-message.success {
|
||||
color: #008000;
|
||||
background-color: #f1f8e9;
|
||||
border: 1px solid #c8e6c9;
|
||||
}
|
||||
|
||||
.birth-date-message.info {
|
||||
color: #2196f3;
|
||||
background-color: #e3f2fd;
|
||||
border: 1px solid #90caf9;
|
||||
}
|
||||
|
||||
/* Disabled calendar cell - 강화된 스타일 */
|
||||
.calendar-table td.disabled {
|
||||
color: #ccc;
|
||||
cursor: not-allowed;
|
||||
pointer-events: none;
|
||||
background: none !important;
|
||||
}
|
||||
|
||||
.calendar-table td.disabled:hover {
|
||||
background: none !important;
|
||||
cursor: not-allowed !important;
|
||||
}
|
||||
|
||||
.flatpickr-calendar {
|
||||
max-width: 320px;
|
||||
margin: 0 auto;
|
||||
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
|
||||
}
|
||||
|
||||
.flatpickr-day.disabled {
|
||||
color: #ccc !important;
|
||||
background: #f8f9fa;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.flatpickr-day.today {
|
||||
background: #007bff;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.flatpickr-day.selected {
|
||||
background: #28a745;
|
||||
border-color: #28a745;
|
||||
}
|
||||
|
||||
|
||||
/* Responsive Design */
|
||||
@media (max-width: 1199.98px) {
|
||||
.reservation-container {
|
||||
max-width: 960px !important;
|
||||
gap: 16px;
|
||||
padding: 20px 15px;
|
||||
}
|
||||
|
||||
.box {
|
||||
padding: 20px 14px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 991.98px) {
|
||||
.reservation-container {
|
||||
max-width: 720px !important;
|
||||
flex-direction: column;
|
||||
gap: 24px;
|
||||
height: auto;
|
||||
padding: 20px 15px;
|
||||
}
|
||||
|
||||
.box {
|
||||
min-height: unset;
|
||||
min-width: unset;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 767.98px) {
|
||||
.reservation-container {
|
||||
max-width: 540px !important;
|
||||
padding: 15px;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.box {
|
||||
padding: 16px 12px;
|
||||
}
|
||||
|
||||
.calendar-table th,
|
||||
.calendar-table td {
|
||||
width: 1.8em;
|
||||
height: 1.8em;
|
||||
}
|
||||
|
||||
.time-btn {
|
||||
min-width: 70px;
|
||||
font-size: 0.95em;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 575.98px) {
|
||||
.reservation-container {
|
||||
max-width: 100% !important;
|
||||
padding: 10px;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.box {
|
||||
padding: 16px 8px;
|
||||
}
|
||||
|
||||
.calendar-table th,
|
||||
.calendar-table td {
|
||||
width: 1.5em;
|
||||
height: 1.5em;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
.time-btn {
|
||||
min-width: 60px;
|
||||
font-size: 0.9em;
|
||||
padding: 6px 0;
|
||||
}
|
||||
|
||||
.step-title {
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
.service-item {
|
||||
font-size: 1em;
|
||||
}
|
||||
}
|
||||
@@ -1,435 +1,471 @@
|
||||
/* main_img */
|
||||
.project_wrap .same main .main_img .text_box > div { width:50%; }
|
||||
.project_wrap .same main .main_img .text_box .right_text_box p { font-size:52px; color:#000; }
|
||||
.project_wrap .same main .main_img .text_box .right_text_box p span { font-weight:700; }
|
||||
.project_wrap .same main .main_img .text_box .left_text_box p { color:#fff; }
|
||||
.project_wrap .same main .main_img .text_box .left_text_box p span { color:#fff; }
|
||||
/* content5 */
|
||||
.project_wrap .same main .content5 { padding-bottom:0; }
|
||||
|
||||
/* 반응형 - 모바일 */
|
||||
@media only screen and (max-width:768px){
|
||||
/* main_img */
|
||||
.project_wrap .same main .content5 { padding-bottom:0; }
|
||||
.project_wrap .same main .main_img .text_box > div { width:100%; }
|
||||
.project_wrap .same main .main_img .text_box .right_text_box { display:none; }
|
||||
.project_wrap .same main .main_img .text_box .left_text_box p br { display:none; }
|
||||
.project_wrap .same main .main_img .text_box .left_text_box p span { color:#000; }
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
|
||||
.fix_area {
|
||||
min-width: 1200px;
|
||||
width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 0 16px;
|
||||
}
|
||||
|
||||
.btn-basic {
|
||||
width:120px;
|
||||
height:40px;
|
||||
border: 1px solid #a73439;
|
||||
border-radius:5px;
|
||||
background-color: white;
|
||||
color: #a73439;
|
||||
}
|
||||
|
||||
.clear:after {
|
||||
display: block;
|
||||
visibility: hidden;
|
||||
height: 0;
|
||||
font-size: 0;
|
||||
clear: both;
|
||||
content: "";
|
||||
}
|
||||
|
||||
|
||||
.clear > .right {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.clear > .left{
|
||||
float:left;
|
||||
}
|
||||
|
||||
.main-img-size {
|
||||
position: relative;
|
||||
width: 655px;
|
||||
height: 368px;
|
||||
overflow: hidden;
|
||||
border-radius: 0.25rem;
|
||||
text-align: center;
|
||||
}
|
||||
.img_center img {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 100%;
|
||||
body {
|
||||
font-family: 'Noto Sans KR', sans-serif;
|
||||
margin: 0;
|
||||
background: #f7f7f9;
|
||||
color: #222;
|
||||
}
|
||||
|
||||
|
||||
.thumbnail-bottom-txt {
|
||||
padding-top: 15px;
|
||||
}
|
||||
|
||||
.thumbnail-bottom-txt > span {
|
||||
display: block;
|
||||
color: #6c696a;
|
||||
font-size: 13.5px;
|
||||
}
|
||||
|
||||
.content>.wp60 {
|
||||
width: 60%;
|
||||
}
|
||||
.content>.wp40 {
|
||||
width: 40%;
|
||||
}
|
||||
|
||||
.wp10{
|
||||
width:10%;
|
||||
}
|
||||
.wp90{
|
||||
width:90%;
|
||||
}
|
||||
|
||||
|
||||
.content>[class^=wp] {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.consultation-info h2{
|
||||
font-size: 4rem;
|
||||
}
|
||||
.consultation-info p{
|
||||
margin-top: 2rem;
|
||||
font-size:2rem;
|
||||
}
|
||||
|
||||
|
||||
.consultation-info .price {
|
||||
display: block;
|
||||
font-size: 3rem;
|
||||
margin-top: 3rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.price-area .border-line {
|
||||
height: 1px;
|
||||
background-color: #ddd;
|
||||
}
|
||||
|
||||
|
||||
.hashtag_list{
|
||||
padding: 1rem 0 1.5rem;
|
||||
}
|
||||
|
||||
.hashtag_list span{
|
||||
font-size:1.6rem;
|
||||
color:#a94442;
|
||||
}
|
||||
|
||||
|
||||
.procedure-area>.procedure_select_txt{
|
||||
float: left;
|
||||
width: 7rem;
|
||||
line-height: 1.5;
|
||||
padding-top: 0.375rem;
|
||||
}
|
||||
|
||||
.procedure-area > .dropdown_area {
|
||||
float: right;
|
||||
width: calc(100% - 7rem);
|
||||
}
|
||||
|
||||
.select_procedure_div {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.select_procedure_div .default_item {
|
||||
padding-top: 0.5rem;
|
||||
padding-left: 0.5rem;
|
||||
padding-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.select_procedure_div .default_item {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
padding-right: 2.5rem;
|
||||
background-color: #fff;
|
||||
border: 1px solid #cb9f76;
|
||||
text-align: left;
|
||||
border-radius: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
|
||||
.option_scrl_wrap {
|
||||
display: none;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
z-index:1;
|
||||
}
|
||||
|
||||
.select_procedure_div.active .option_scrl_wrap {
|
||||
border: 1px solid #76232f;
|
||||
}
|
||||
|
||||
.select_service_form.active .default_item:after {
|
||||
border-top-color: #333;
|
||||
}
|
||||
|
||||
.default_item:after {
|
||||
content: '';
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: 1.1em;
|
||||
margin-top: -0.2rem;
|
||||
border: 0.28571428em solid transparent;
|
||||
border-top-color: #999;
|
||||
}
|
||||
|
||||
|
||||
.optipon_item {
|
||||
padding: 0.4rem 0.5rem;
|
||||
}
|
||||
|
||||
.sepr_wrap {
|
||||
position: relative;
|
||||
min-height: 2.5rem;
|
||||
}
|
||||
|
||||
.item_subprice {
|
||||
min-height: 42px;
|
||||
}
|
||||
|
||||
.select_procedure_div.active .option_scrl_wrap {
|
||||
display: block;
|
||||
position: absolute;
|
||||
right: -1px;
|
||||
left: -1px;
|
||||
background-color: #fff;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.cs-checkbox > label.d-block {
|
||||
display: block;
|
||||
}
|
||||
.cs-checkbox > label {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
padding-left: 30px;
|
||||
margin: 0;
|
||||
line-height: 20px;
|
||||
transition: color .3s ease-out;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.oi-wrap {
|
||||
display: -webkit-box;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
-webkit-box-pack: justify;
|
||||
-ms-flex-pack: justify;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
/* .cs-checkbox input[type="checkbox"] { */
|
||||
/* visibility: hidden; */
|
||||
/* display: none; */
|
||||
/* } */
|
||||
|
||||
/* .cs-checkbox > label:before, .cs-radio > .r_visible { */
|
||||
/* border: 1px solid #ccc; */
|
||||
/* } */
|
||||
|
||||
input[type="checkbox"] {
|
||||
transform: scale(1.5); /* 크기를 1.5배로 확대 */
|
||||
}
|
||||
|
||||
.idxChk{
|
||||
position:absolute;
|
||||
}
|
||||
|
||||
.cs-checkbox > label:before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
display: inline-block;
|
||||
margin-right: 0.8em;
|
||||
border-radius: 0;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.oi-wrap .oi-txt {
|
||||
flex: 0 0 65%;
|
||||
max-width: 65%;
|
||||
}
|
||||
|
||||
.option_list {
|
||||
display: block;
|
||||
#service-header {
|
||||
background: #fff;
|
||||
max-height: 224px;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
border-bottom: 1px solid #ececec;
|
||||
padding: 14px 0 14px 24px;
|
||||
font-size: 0.98em;
|
||||
color: #888;
|
||||
}
|
||||
|
||||
|
||||
.oi-wrap .oi-price {
|
||||
flex: 0 0 35%;
|
||||
max-width: 35%;
|
||||
text-align: right;
|
||||
.main-wrap {
|
||||
max-width: 1280px;
|
||||
margin: 0 auto;
|
||||
background: #fff;
|
||||
border-radius: 18px;
|
||||
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.07);
|
||||
margin-top: 32px;
|
||||
padding: 0 0 32px 0;
|
||||
}
|
||||
|
||||
.original_price {
|
||||
color: #757575;
|
||||
opacity: 0.7;
|
||||
.top-section {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 32px;
|
||||
padding: 32px 32px 0 32px;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.d-block {
|
||||
display: block;
|
||||
.img-box {
|
||||
border-radius: 18px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.discount_price {
|
||||
font-family: 'Campton', Sans-serif;
|
||||
font-weight: 600;
|
||||
color: #A73439;
|
||||
}
|
||||
|
||||
.procedure-area:after {
|
||||
display: block;
|
||||
visibility: hidden;
|
||||
height: 0;
|
||||
font-size: 0;
|
||||
clear: both;
|
||||
content: "";
|
||||
}
|
||||
|
||||
.selected-procedure{
|
||||
margin-top:20px;
|
||||
position:relative;
|
||||
}
|
||||
|
||||
|
||||
.selected-procedure .selt_info_wrap .info {
|
||||
top: 0.7rem;
|
||||
width: 9.5em;
|
||||
padding-top: 0.3em;
|
||||
}
|
||||
|
||||
.selt_info_wrap .info {
|
||||
position: absolute;
|
||||
top: 0.2rem;
|
||||
right: 0.2rem;
|
||||
width: 8.5em;
|
||||
padding-right: 1rem;
|
||||
padding-top: 0.2em;
|
||||
text-align: right;
|
||||
}
|
||||
.selt_info_wrap .info button{
|
||||
border:none;
|
||||
background-color:#fff;
|
||||
}
|
||||
|
||||
|
||||
.real_price {
|
||||
font-family: 'Campton', Sans-serif;
|
||||
font-weight: 600;
|
||||
color: #A73439;
|
||||
}
|
||||
|
||||
.selected-procedure .selt_info_wrap {
|
||||
padding: 0.8rem 0;
|
||||
z-index:0;
|
||||
}
|
||||
|
||||
.selt_info_wrap {
|
||||
position: relative;
|
||||
min-height: 2.5rem;
|
||||
background-color:#fff;
|
||||
border:1px solid #eee;
|
||||
}
|
||||
|
||||
.selected-procedure .selt_info_wrap .selt {
|
||||
padding-right: 9.5em;
|
||||
padding-left: 1rem;
|
||||
.img-box img {
|
||||
width: 100%;
|
||||
object-fit: cover;
|
||||
border-radius: 18px;
|
||||
}
|
||||
|
||||
.selt_info_wrap .selt {
|
||||
padding-top: 0.3em;
|
||||
padding-right: 8.5em;
|
||||
.info-box {
|
||||
flex: 1 1 300px;
|
||||
min-width: 240px;
|
||||
}
|
||||
|
||||
.total-price-area .total {
|
||||
position: relative;
|
||||
background-color: #e6e6e6;
|
||||
padding: 25px 25px 60px;
|
||||
.info-title {
|
||||
font-size: 1.7em;
|
||||
font-weight: 700;
|
||||
margin-bottom: 6px;
|
||||
color: #222;
|
||||
}
|
||||
|
||||
.info-desc {
|
||||
color: #444;
|
||||
font-size: 1.1em;
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
|
||||
.event-period {
|
||||
font-size: 1.05em;
|
||||
font-weight: 600;
|
||||
color: #e74c3c;
|
||||
background: #fef2f2;
|
||||
border: 1px solid #fecaca;
|
||||
border-radius: 8px;
|
||||
padding: 10px 14px;
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
|
||||
.info-price {
|
||||
font-size: 1.2em;
|
||||
font-weight: 700;
|
||||
color: #b23c3c;
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
|
||||
.select-row {
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
|
||||
.select-row label {
|
||||
font-weight: 500;
|
||||
margin-bottom: 8px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.total-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
font-size: 1.1em;
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
|
||||
.total-price-area .total .txt_sub {
|
||||
color: #222222;
|
||||
font-size: 2rem;
|
||||
opacity: 1;
|
||||
}
|
||||
.total-price-area .total .right strong {
|
||||
font-size: 22px;
|
||||
font-weight: 600;
|
||||
.total-row .total-label {
|
||||
color: #888;
|
||||
}
|
||||
|
||||
.total-price-area .total-price-txt {
|
||||
font-family: 'Campton', Sans-serif;
|
||||
font-weight: 600;
|
||||
color: #A73439;
|
||||
.total-row .total-price {
|
||||
font-weight: bold;
|
||||
color: #b23c3c;
|
||||
}
|
||||
|
||||
.total-price-area .total .bs-txt {
|
||||
position: absolute;
|
||||
bottom: 17px;
|
||||
right: 25px;
|
||||
display: block;
|
||||
margin-top: 4px;
|
||||
font-size: 12px;
|
||||
color: #999999;
|
||||
letter-spacing: -1px;
|
||||
}
|
||||
|
||||
|
||||
.main_btn {
|
||||
text-align: right;
|
||||
margin: 1rem 0;
|
||||
}
|
||||
|
||||
.main_btn button {
|
||||
width: 180px;
|
||||
height: 50px;
|
||||
}
|
||||
|
||||
|
||||
.fastrack-btn {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
border: 1px solid #a73439;
|
||||
background-color: #a73439;
|
||||
.reserve-btn {
|
||||
width: 100%;
|
||||
padding: 14px 0;
|
||||
background: #b23c3c;
|
||||
color: #fff;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
font-size: 1.1em;
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
transition: background 0.15s;
|
||||
}
|
||||
|
||||
.img-content{
|
||||
background-color: #eee;
|
||||
.reserve-btn:disabled {
|
||||
background: #ddd;
|
||||
color: #888;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.img-area{
|
||||
padding-top: 70px;
|
||||
padding-bottom: 200px;
|
||||
text-align:center;
|
||||
.desc-section {
|
||||
margin-top: 36px;
|
||||
padding: 0 32px;
|
||||
}
|
||||
|
||||
.desc-title {
|
||||
font-size: 1.25em;
|
||||
font-weight: 700;
|
||||
margin-bottom: 12px;
|
||||
color: #222;
|
||||
}
|
||||
|
||||
.desc-content {
|
||||
color: #444;
|
||||
font-size: 1.05em;
|
||||
line-height: 1.7;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.hashtag-section {
|
||||
margin-top: 30px;
|
||||
padding: 20px 0;
|
||||
border-top: 1px solid #eee;
|
||||
}
|
||||
|
||||
.hashtag-container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
.hashtag-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.hashtag {
|
||||
display: inline-block;
|
||||
background-color: #f8f9fa;
|
||||
color: #495057;
|
||||
padding: 8px 15px;
|
||||
border-radius: 20px;
|
||||
font-size: 14px;
|
||||
text-decoration: none;
|
||||
border: 1px solid #dee2e6;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.hashtag:hover {
|
||||
background-color: #007bff;
|
||||
color: white;
|
||||
border-color: #007bff;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#thumbnail-bottom-txt {
|
||||
padding: 8px;
|
||||
margin-top: 10px;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
/* Choices.js 커스터마이징 - 개선된 버전 */
|
||||
.choices {
|
||||
border: 1px solid #ddd !important;
|
||||
border-radius: 6px !important;
|
||||
font-size: 1em !important;
|
||||
background: #fff !important;
|
||||
min-width: 300px !important;
|
||||
}
|
||||
|
||||
.choices__inner {
|
||||
background: #fff !important;
|
||||
padding: 8px 12px !important;
|
||||
min-height: 40px !important;
|
||||
color: #222 !important;
|
||||
min-width: 280px !important;
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
/* 플레이스홀더 텍스트 잘림 방지 */
|
||||
.choices__placeholder {
|
||||
color: #888 !important;
|
||||
opacity: 1 !important;
|
||||
width: 100% !important;
|
||||
min-width: 200px !important;
|
||||
white-space: nowrap !important;
|
||||
overflow: visible !important;
|
||||
text-overflow: clip !important;
|
||||
display: block !important;
|
||||
}
|
||||
|
||||
.choices__input {
|
||||
color: #222 !important;
|
||||
background: transparent !important;
|
||||
min-width: 200px !important;
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
.choices__input::placeholder {
|
||||
color: #888 !important;
|
||||
opacity: 1 !important;
|
||||
width: 100% !important;
|
||||
min-width: 200px !important;
|
||||
white-space: nowrap !important;
|
||||
overflow: visible !important;
|
||||
}
|
||||
|
||||
/* 다중 선택시 입력 필드 너비 확보 */
|
||||
.choices[data-type*="select-multiple"] .choices__input {
|
||||
min-width: 200px !important;
|
||||
width: auto !important;
|
||||
flex: 1 !important;
|
||||
}
|
||||
|
||||
/* 선택된 항목들과 입력 필드 공간 분배 */
|
||||
.choices__list--multiple {
|
||||
flex-wrap: wrap !important;
|
||||
align-items: center !important;
|
||||
}
|
||||
|
||||
.choices__list--multiple:empty+.choices__input {
|
||||
min-width: 200px !important;
|
||||
width: 100% !important;
|
||||
flex: 1 !important;
|
||||
}
|
||||
|
||||
/* 선택된 항목의 가격 표시 스타일 */
|
||||
.selected-item-content {
|
||||
display: flex !important;
|
||||
flex-direction: column !important;
|
||||
align-items: flex-start !important;
|
||||
flex: 1 !important;
|
||||
}
|
||||
|
||||
.selected-item-name {
|
||||
font-weight: 500 !important;
|
||||
margin-bottom: 2px !important;
|
||||
}
|
||||
|
||||
.selected-item-price {
|
||||
font-size: 0.85em !important;
|
||||
}
|
||||
|
||||
.selected-price {
|
||||
color: #b23c3c !important;
|
||||
font-weight: bold !important;
|
||||
}
|
||||
|
||||
.selected-price-discount {
|
||||
color: #b23c3c !important;
|
||||
font-weight: bold !important;
|
||||
font-size: 0.9em !important;
|
||||
}
|
||||
|
||||
.selected-price-original {
|
||||
color: #aaa !important;
|
||||
font-size: 0.85em !important;
|
||||
text-decoration: line-through !important;
|
||||
margin-left: 4px !important;
|
||||
}
|
||||
|
||||
/* 선택된 항목들 스타일 수정 */
|
||||
.choices__list--multiple .choices__item {
|
||||
background-color: #f8f9fa !important;
|
||||
border: 1px solid #dee2e6 !important;
|
||||
border-radius: 16px !important;
|
||||
padding: 8px 12px !important;
|
||||
margin: 2px !important;
|
||||
font-size: 0.9em !important;
|
||||
color: #495057 !important;
|
||||
flex-shrink: 0 !important;
|
||||
display: flex !important;
|
||||
align-items: center !important;
|
||||
}
|
||||
|
||||
/* 빨간색으로 변경 */
|
||||
.choices__button {
|
||||
background-image: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjEiIGhlaWdodD0iMjEiIHZpZXdCb3g9IjAgMCAyMSAyMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48ZyBmaWxsPSIjYjIzYzNjIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPjxwYXRoIGQ9Ik0yLjU5Mi4wNDRsMTguMzY0IDE4LjM2NC0yLjU0OCAyLjU0OEwuMDQ0IDIuNTkyeiIvPjxwYXRoIGQ9Ik0wIDE4LjM2NEwxOC4zNjQgMGwyLjU0OCAyLjU0OEwyLjU0OCAyMC45MTJ6Ii8+PC9nPjwvc3ZnPg==') !important;
|
||||
background-size: 14px 14px !important;
|
||||
background-position: center !important;
|
||||
background-repeat: no-repeat !important;
|
||||
border-left: 0 !important;
|
||||
margin: 0 !important;
|
||||
padding: 0 !important;
|
||||
width: 14px !important;
|
||||
}
|
||||
|
||||
/* 드롭다운 컨테이너 */
|
||||
.choices__list--dropdown {
|
||||
background: #fff !important;
|
||||
border: 1px solid #ddd !important;
|
||||
border-radius: 6px !important;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1) !important;
|
||||
}
|
||||
|
||||
/* 드롭다운 옵션들 스타일 */
|
||||
.choices__list--dropdown .choices__item {
|
||||
padding: 8px 12px !important;
|
||||
display: flex !important;
|
||||
justify-content: space-between !important;
|
||||
align-items: center !important;
|
||||
color: #222 !important;
|
||||
}
|
||||
|
||||
.choices__list--dropdown .choices__item--selectable:hover {
|
||||
background-color: #f5f5f5 !important;
|
||||
color: #222 !important;
|
||||
}
|
||||
|
||||
.choices__list--dropdown .choices__item--highlighted {
|
||||
background-color: #b23c3c !important;
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
/* 포커스 상태 */
|
||||
.choices.is-focused .choices__inner {
|
||||
border-color: #b23c3c !important;
|
||||
}
|
||||
|
||||
/* 가격 표시 스타일 */
|
||||
.procedure-price {
|
||||
color: #b23c3c !important;
|
||||
font-weight: bold !important;
|
||||
font-size: 0.9em !important;
|
||||
}
|
||||
|
||||
.procedure-price-discount {
|
||||
color: #b23c3c !important;
|
||||
font-weight: bold !important;
|
||||
font-size: 0.9em !important;
|
||||
}
|
||||
|
||||
.procedure-price-original {
|
||||
color: #aaa !important;
|
||||
font-size: 0.85em !important;
|
||||
text-decoration: line-through !important;
|
||||
margin-left: 4px !important;
|
||||
}
|
||||
|
||||
/* 드롭다운이 열렸을 때 하이라이트된 항목의 가격 색상 */
|
||||
.choices__list--dropdown .choices__item--highlighted .procedure-price,
|
||||
.choices__list--dropdown .choices__item--highlighted .procedure-price-discount {
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
.choices__list--dropdown .choices__item--highlighted .procedure-price-original {
|
||||
color: #ddd !important;
|
||||
}
|
||||
|
||||
/* 검색 입력창 스타일 */
|
||||
.choices[data-type*="select-multiple"] .choices__input {
|
||||
background-color: transparent !important;
|
||||
color: #222 !important;
|
||||
}
|
||||
|
||||
/* 비활성화 상태 */
|
||||
.choices.is-disabled .choices__inner {
|
||||
background-color: #f8f9fa !important;
|
||||
color: #6c757d !important;
|
||||
cursor: not-allowed !important;
|
||||
}
|
||||
|
||||
/* 반응형 대응 */
|
||||
@media (max-width: 600px) {
|
||||
.choices {
|
||||
min-width: 250px !important;
|
||||
}
|
||||
|
||||
.choices__inner {
|
||||
min-width: 230px !important;
|
||||
}
|
||||
|
||||
.choices__placeholder,
|
||||
.choices__input,
|
||||
.choices__input::placeholder {
|
||||
min-width: 150px !important;
|
||||
}
|
||||
|
||||
.choices__list--multiple .choices__item {
|
||||
max-width: 250px !important;
|
||||
padding: 6px 10px !important;
|
||||
}
|
||||
|
||||
.selected-item-name {
|
||||
font-size: 0.9em !important;
|
||||
}
|
||||
|
||||
.selected-item-price {
|
||||
font-size: 0.8em !important;
|
||||
}
|
||||
|
||||
.choices__list--multiple .choices__item[data-deletable] .choices__button {
|
||||
width: 20px !important;
|
||||
height: 20px !important;
|
||||
font-size: 16px !important;
|
||||
text-indent: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.main-wrap {
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.top-section {
|
||||
flex-direction: column;
|
||||
gap: 18px;
|
||||
padding: 20px 10px 0 10px;
|
||||
}
|
||||
|
||||
.img-box {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.info-box {
|
||||
min-width: unset;
|
||||
}
|
||||
|
||||
.desc-section {
|
||||
padding: 0 10px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.main-wrap {
|
||||
margin-top: 0;
|
||||
border-radius: 0;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.top-section {
|
||||
padding: 12px 2vw 0 2vw;
|
||||
}
|
||||
|
||||
.desc-section {
|
||||
padding: 0 2vw;
|
||||
}
|
||||
}
|
||||
@@ -1,110 +1,495 @@
|
||||
/* main_img */
|
||||
.project_wrap .same main .main_img .text_box > div { width:50%; }
|
||||
.project_wrap .same main .main_img .text_box .right_text_box p { font-size:52px; color:#000; }
|
||||
.project_wrap .same main .main_img .text_box .right_text_box p span { font-weight:700; }
|
||||
.project_wrap .same main .main_img .text_box .left_text_box p { color:#fff; }
|
||||
.project_wrap .same main .main_img .text_box .left_text_box p span { color:#fff; }
|
||||
/* content5 */
|
||||
.project_wrap .same main .content5 { padding-bottom:0; }
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.project_wrap .same aside {background: white; width: 30%; top: 12%;}
|
||||
.project_wrap .same aside ul{position: fixed; left: 5%; width: 19%;}
|
||||
.project_wrap .same aside ul li{margin-bottom: 0.5rem; margin-top: 0px;}
|
||||
.project_wrap .same aside .first {padding-top: 1rem; padding-bottom: 1rem;}
|
||||
.project_wrap .same aside .nonactive {border: 1px solid #d8d8d8; background-color: #f9f9fb;}
|
||||
.project_wrap .same aside .active {border: 1px solid #d8d8d8; color: #a73439; background-color: rgba(118, 35, 47, 0); border-left: 2px solid #a73439;}
|
||||
.project_wrap .same aside ul a{padding-left: 10px;}
|
||||
.project_wrap .same main {width: 60%; padding-top: 7%;}
|
||||
.project_wrap .same main ul{padding-top: 2%; width:102%;}
|
||||
.event-card-list > ul { display: block; list-style-type: disc; margin-block-start: 1em; margin-block-end: 1em; padding-inline-start: 40px; unicode-bidi: isolate; }
|
||||
.event-card-list > li { margin-bottom: 70px; opacity: 1;}
|
||||
.event-card-list .event-card { display: block; position: relative; width: 775px; height: 196px; padding-right: 10px; margin-left: auto; overflow: hidden;}
|
||||
.event-card .img_box { position: relative; width: 350px; height: 100%; background-color: black; float: left; overflow: hidden; }
|
||||
.event-card .img_box img { position: absolute; min-width: 100%; min-height: 100%; width: 100%; height: 100%; margin: auto; top: 0; bottom: 0; left: 0; right: 0; opacity: 1; transform: scale(1); transition: .4s ease-out;}
|
||||
.event-card .txt-box { position: relative; padding-top: 115px; width: 343px; height: 100%; float: right;}
|
||||
.event-card .txt-box .tit-txt { position: absolute; left: 0; bottom: 88px;}
|
||||
.txt-box .tit-txt > p { margin: 0; font-size: 22px; font-weight: 500; line-height: 1.3;}
|
||||
.event-card .txt-box > .sub-txt { font-size: 12px;}
|
||||
.one-ellip { position: relative;overflow: hidden; white-space: nowrap; text-overflow: ellipsis; left: 0%; width: 100%;-webkit-transition: left 3s, width 3s; -moz-transition: left 3s, width 3s; transition: left 3s, width 3s;}
|
||||
.event-card .txt-box .ab_cont { padding-top: 10px;}
|
||||
.ab_cont .cost { font-size: 13px; font-weight: 400; color: #7D7971;}
|
||||
del { text-decoration: line-through;}
|
||||
.ab_cont .discount { display: block; font-size: 1rem;}
|
||||
.ab_cont .discount .txt_num { font-size: 1.6em; font-weight: 600; color: #9F2A2A;}
|
||||
.event-card .txt-box .tit-txt:After { content: ''; position: absolute; top: -49px; left: 0; width: 18px; height: 35px; background: center / contain url(https://www.toxnfill2.com/imges/ico-toxnfill-g.png) no-repeat;}
|
||||
.event-card-list .event-card:after { content: ''; position: absolute; bottom: 9px; right: 0; width: 58px; height: 15px; background: center / contain url(https://www.toxnfill2.com/imges/long-arrow-right-g.png) no-repeat;}
|
||||
.add-ticket-btn {
|
||||
font-size: 1.2rem;
|
||||
font-weight: 400;
|
||||
letter-spacing: -0.03em;
|
||||
padding: .35rem 1rem;
|
||||
border: 1px solid #ced4da;
|
||||
border-radius: 50em;
|
||||
transition: 0.2s;
|
||||
background:white;
|
||||
/* 전체 기본 스타일 */
|
||||
html,
|
||||
body {
|
||||
height: 100vh;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-family: 'Pretendard', -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', sans-serif;
|
||||
background: #f8f9fa;
|
||||
color: #1a1a1a;
|
||||
overflow-x: hidden;
|
||||
font-size: 16px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
.right { float: right;}
|
||||
.left { float: left;}
|
||||
.card_otxt {
|
||||
position: relative;
|
||||
display: block;
|
||||
border-radius: 1rem;
|
||||
padding: 1rem;
|
||||
}
|
||||
.border {
|
||||
border: 1px solid #ddd;
|
||||
}
|
||||
.__card_otxt {
|
||||
height: 95px;
|
||||
}
|
||||
.row>.col2 {
|
||||
width: 50%;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
.row.row_padding>.col2, .row.row_padding>.col3, .row.row_padding>.col4, .row.row_padding>.col5 {
|
||||
padding-right: 1rem;
|
||||
padding-left: 1rem;
|
||||
}
|
||||
.row.row_padding {
|
||||
margin-left: -1rem;
|
||||
margin-right: -1rem;
|
||||
|
||||
/* 메인 컨테이너 */
|
||||
.container {
|
||||
max-width: 1280px;
|
||||
width: 100%;
|
||||
margin: 0 auto;
|
||||
min-height: calc(100vh - 300px);
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
flex-direction: column;
|
||||
gap: 1.5rem;
|
||||
}
|
||||
.border-b {
|
||||
border-bottom: 1px solid #ddd;
|
||||
height:5%;
|
||||
|
||||
/* 상단 헤더 영역 */
|
||||
.header {
|
||||
background: white;
|
||||
border-radius: 16px;
|
||||
box-shadow: 0 2px 16px rgba(0, 0, 0, 0.06);
|
||||
padding: 1rem 2rem;
|
||||
border-bottom: 3px solid #C60B24;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.txt_num {
|
||||
font-family: 'Campton', Sans-serif;
|
||||
|
||||
.page-title {
|
||||
font-size: clamp(1.5rem, 3vw, 1.875rem);
|
||||
font-weight: 700;
|
||||
color: #1a1a1a;
|
||||
margin: 0;
|
||||
letter-spacing: -0.025em;
|
||||
}
|
||||
|
||||
/* 하단 콘텐츠 영역 (사이드바 + 메인) */
|
||||
.content-wrapper {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
gap: 1.5rem;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
/* 좌측 사이드바 */
|
||||
.sidebar {
|
||||
width: 240px;
|
||||
min-width: 220px;
|
||||
flex-shrink: 0;
|
||||
background: white;
|
||||
border-radius: 16px;
|
||||
box-shadow: 0 2px 16px rgba(0, 0, 0, 0.06);
|
||||
height: fit-content;
|
||||
max-height: 100%;
|
||||
overflow-y: auto;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.sidebar-header {
|
||||
padding: 1.25rem 1.5rem;
|
||||
font-size: 1.125rem;
|
||||
font-weight: 700;
|
||||
color: #C60B24;
|
||||
border-bottom: 1px solid #e9ecef;
|
||||
}
|
||||
|
||||
.category-list {
|
||||
list-style: none;
|
||||
padding: 1rem;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.category-item {
|
||||
margin-bottom: 0.375rem;
|
||||
}
|
||||
|
||||
.category-link {
|
||||
display: block;
|
||||
width: 100%;
|
||||
padding: 0.75rem 1rem;
|
||||
font-size: 0.95rem;
|
||||
color: #6b7280;
|
||||
text-decoration: none;
|
||||
background: none;
|
||||
border: none;
|
||||
border-radius: 10px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.category-link:hover {
|
||||
background: #f9fafb;
|
||||
color: #C60B24;
|
||||
transform: translateX(2px);
|
||||
}
|
||||
|
||||
.category-link.active {
|
||||
background: rgba(198, 11, 36, 0.08);
|
||||
color: #C60B24;
|
||||
font-weight: 600;
|
||||
color: #A73439;
|
||||
border-left: 4px solid #C60B24;
|
||||
}
|
||||
.card_otxt .ab_cont {
|
||||
|
||||
/* 메인 콘텐츠 영역 */
|
||||
.main-content {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
/* 이벤트 리스트 */
|
||||
.event-list {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding-right: 0.5rem;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.event-grid {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1.25rem;
|
||||
padding-bottom: 2rem;
|
||||
}
|
||||
|
||||
/* 이벤트 카드 */
|
||||
.event-card {
|
||||
display: flex;
|
||||
background: white;
|
||||
border-radius: 16px;
|
||||
box-shadow: 0 2px 16px rgba(0, 0, 0, 0.06);
|
||||
overflow: hidden;
|
||||
transition: all 0.3s ease;
|
||||
border: 1px solid #f1f5f9;
|
||||
min-height: 160px;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.event-card:hover {
|
||||
transform: translateY(-4px);
|
||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.12);
|
||||
border-color: #C60B24;
|
||||
}
|
||||
|
||||
/* 지난 이벤트 스타일 */
|
||||
.event-card.expired {
|
||||
opacity: 0.6;
|
||||
filter: grayscale(30%);
|
||||
}
|
||||
|
||||
.event-card.expired:hover {
|
||||
transform: none;
|
||||
box-shadow: 0 2px 16px rgba(0, 0, 0, 0.06);
|
||||
border-color: #9ca3af;
|
||||
}
|
||||
|
||||
.expired-badge {
|
||||
position: absolute;
|
||||
right: 1rem;
|
||||
top: 3.4rem;
|
||||
width: 8.5em;
|
||||
text-align: right;
|
||||
top: 12px;
|
||||
right: 12px;
|
||||
background: rgba(107, 114, 128, 0.9);
|
||||
color: #fff;
|
||||
padding: 4px 12px;
|
||||
border-radius: 20px;
|
||||
font-size: 0.8rem;
|
||||
font-weight: 600;
|
||||
z-index: 2;
|
||||
}
|
||||
.card_otxt .fix_cont {
|
||||
padding-right: 11.5rem;
|
||||
|
||||
.event-img {
|
||||
width: 340px;
|
||||
min-width: 280px;
|
||||
background: #f3f4f6;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
overflow: hidden;
|
||||
}
|
||||
.card_otxt .fix_cont p {
|
||||
|
||||
.event-img img {
|
||||
width: 100%;
|
||||
object-fit: cover;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.event-info {
|
||||
flex: 1;
|
||||
padding: 1.5rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.event-title {
|
||||
font-size: 1.25rem;
|
||||
font-weight: 700;
|
||||
margin-bottom: 0.5rem;
|
||||
color: #1a1a1a;
|
||||
letter-spacing: -0.025em;
|
||||
}
|
||||
|
||||
.event-desc {
|
||||
color: #6b7280;
|
||||
font-size: 0.9375rem;
|
||||
margin-bottom: 0.75rem;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.event-meta {
|
||||
font-size: 0.875rem;
|
||||
color: #9ca3af;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.event-date {
|
||||
font-size: 0.85rem;
|
||||
color: #9ca3af;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.event-price {
|
||||
font-size: 1.125rem;
|
||||
color: #C60B24;
|
||||
font-weight: 700;
|
||||
margin-bottom: 0;
|
||||
word-break: keep-all;
|
||||
}
|
||||
.mb3{margin-bottom: 3rem;}
|
||||
.mt70{margin-top: 70%;}
|
||||
|
||||
|
||||
/* 반응형 - 모바일 */
|
||||
@media only screen and (max-width:768px){
|
||||
/* main_img */
|
||||
.project_wrap .same main .content5 { padding-bottom:0; }
|
||||
.project_wrap .same main .main_img .text_box > div { width:100%; }
|
||||
.project_wrap .same main .main_img .text_box .right_text_box { display:none; }
|
||||
.project_wrap .same main .main_img .text_box .left_text_box p br { display:none; }
|
||||
.project_wrap .same main .main_img .text_box .left_text_box p span { color:#000; }
|
||||
.project_wrap .same main {width: 100%;}
|
||||
/* 로딩 및 에러 메시지 */
|
||||
.loading {
|
||||
text-align: center;
|
||||
padding: 2rem;
|
||||
color: #6b7280;
|
||||
font-size: 0.9375rem;
|
||||
}
|
||||
|
||||
.error-message {
|
||||
text-align: center;
|
||||
padding: 2rem;
|
||||
color: #dc2626;
|
||||
font-size: 0.9375rem;
|
||||
}
|
||||
|
||||
/* 레이어 팝업 */
|
||||
.popup-overlay {
|
||||
display: none;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
z-index: 9999;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.popup-overlay.active {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.popup-content {
|
||||
background: white;
|
||||
border-radius: 16px;
|
||||
padding: 2rem 2.5rem;
|
||||
text-align: center;
|
||||
box-shadow: 0 8px 40px rgba(0, 0, 0, 0.2);
|
||||
max-width: 400px;
|
||||
width: 90%;
|
||||
animation: popupIn 0.25s ease-out;
|
||||
}
|
||||
|
||||
@keyframes popupIn {
|
||||
from {
|
||||
transform: scale(0.9);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
to {
|
||||
transform: scale(1);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.popup-icon {
|
||||
font-size: 3rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.popup-title {
|
||||
font-size: 1.25rem;
|
||||
font-weight: 700;
|
||||
color: #1a1a1a;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.popup-message {
|
||||
font-size: 0.95rem;
|
||||
color: #6b7280;
|
||||
margin-bottom: 1.5rem;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.popup-close-btn {
|
||||
background: #C60B24;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
padding: 0.75rem 2rem;
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
|
||||
.popup-close-btn:hover {
|
||||
background: #a5091e;
|
||||
}
|
||||
|
||||
/* 스크롤바 커스터마이징 */
|
||||
.event-list::-webkit-scrollbar,
|
||||
.sidebar::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
|
||||
.event-list::-webkit-scrollbar-track,
|
||||
.sidebar::-webkit-scrollbar-track {
|
||||
background: #f8fafc;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.event-list::-webkit-scrollbar-thumb,
|
||||
.sidebar::-webkit-scrollbar-thumb {
|
||||
background: #cbd5e1;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.event-list::-webkit-scrollbar-thumb:hover,
|
||||
.sidebar::-webkit-scrollbar-thumb:hover {
|
||||
background: #C60B24;
|
||||
}
|
||||
|
||||
/* 반응형 디자인 - 태블릿 */
|
||||
@media (max-width: 1024px) {
|
||||
.container {
|
||||
padding: 1rem;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.content-wrapper {
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
width: 220px;
|
||||
min-width: 200px;
|
||||
}
|
||||
|
||||
.event-img {
|
||||
width: 220px;
|
||||
min-width: 220px;
|
||||
}
|
||||
|
||||
.event-img img {
|
||||
height: 140px;
|
||||
}
|
||||
|
||||
.header {
|
||||
padding: 1.25rem 1.5rem;
|
||||
}
|
||||
|
||||
.page-title {
|
||||
font-size: clamp(1.375rem, 2.8vw, 1.75rem);
|
||||
}
|
||||
}
|
||||
|
||||
/* 반응형 디자인 - 모바일 */
|
||||
@media (max-width: 768px) {
|
||||
.container {
|
||||
flex-direction: column;
|
||||
padding: 1rem;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.content-wrapper {
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
width: 100%;
|
||||
position: static;
|
||||
max-height: none;
|
||||
order: 1;
|
||||
}
|
||||
|
||||
.category-list {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
|
||||
gap: 0.5rem;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.category-link {
|
||||
text-align: center;
|
||||
padding: 0.75rem 0.5rem;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.main-content {
|
||||
order: 2;
|
||||
}
|
||||
|
||||
.event-list {
|
||||
overflow-y: visible;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.event-card {
|
||||
flex-direction: column;
|
||||
min-height: auto;
|
||||
}
|
||||
|
||||
.event-img {
|
||||
width: 100%;
|
||||
min-width: auto;
|
||||
}
|
||||
|
||||
.event-img img {
|
||||
height: 200px;
|
||||
}
|
||||
|
||||
.event-info {
|
||||
padding: 1.25rem;
|
||||
}
|
||||
|
||||
.header {
|
||||
padding: 1rem 1.5rem;
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
.page-title {
|
||||
font-size: clamp(1.25rem, 2.5vw, 1.5rem);
|
||||
}
|
||||
|
||||
.event-title {
|
||||
font-size: 1.125rem;
|
||||
}
|
||||
|
||||
.event-price {
|
||||
font-size: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* 작은 모바일 */
|
||||
@media (max-width: 480px) {
|
||||
.container {
|
||||
padding: 0.75rem;
|
||||
}
|
||||
|
||||
.header {
|
||||
padding: 0.875rem 1rem;
|
||||
}
|
||||
|
||||
.page-title {
|
||||
font-size: clamp(1.125rem, 2.2vw, 1.375rem);
|
||||
}
|
||||
|
||||
.event-info {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.event-title {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.event-desc {
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,255 @@
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
height: 100vh;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-family: 'Pretendard', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
||||
background: #f8f9fa;
|
||||
color: #1a1a1a;
|
||||
overflow-x: hidden;
|
||||
font-size: 16px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 900px;
|
||||
width: 100%;
|
||||
margin: 0 auto;
|
||||
min-height: calc(100vh - 300px);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
/* 브레드크럼 */
|
||||
.breadcrumb {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
font-size: 0.875rem;
|
||||
color: #9ca3af;
|
||||
padding: 0.5rem 0;
|
||||
}
|
||||
|
||||
.breadcrumb a {
|
||||
color: #6b7280;
|
||||
text-decoration: none;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
|
||||
.breadcrumb a:hover {
|
||||
color: #C60B24;
|
||||
}
|
||||
|
||||
/* 게시글 */
|
||||
.review-article {
|
||||
background: white;
|
||||
border-radius: 16px;
|
||||
box-shadow: 0 2px 16px rgba(0, 0, 0, 0.06);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.review-header {
|
||||
padding: 2rem 2rem 1.5rem;
|
||||
border-bottom: 1px solid #f1f5f9;
|
||||
}
|
||||
|
||||
.review-title {
|
||||
font-size: clamp(1.375rem, 3vw, 1.75rem);
|
||||
font-weight: 700;
|
||||
color: #1a1a1a;
|
||||
margin: 0 0 1rem;
|
||||
letter-spacing: -0.025em;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.review-meta {
|
||||
display: flex;
|
||||
gap: 1.5rem;
|
||||
font-size: 0.875rem;
|
||||
color: #9ca3af;
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.review-tags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.review-tag {
|
||||
display: inline-block;
|
||||
padding: 0.25rem 0.75rem;
|
||||
background: rgba(198, 11, 36, 0.08);
|
||||
color: #C60B24;
|
||||
border-radius: 20px;
|
||||
font-size: 0.8125rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* Quill 에디터 본문 */
|
||||
.review-content {
|
||||
padding: 2rem;
|
||||
min-height: 300px;
|
||||
}
|
||||
|
||||
.review-content img {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
border-radius: 8px;
|
||||
margin: 0.5rem 0;
|
||||
}
|
||||
|
||||
.review-content p {
|
||||
margin: 0 0 0.75rem;
|
||||
line-height: 1.8;
|
||||
}
|
||||
|
||||
.review-content h1,
|
||||
.review-content h2,
|
||||
.review-content h3 {
|
||||
margin: 1.5rem 0 0.75rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.review-content blockquote {
|
||||
border-left: 4px solid #C60B24;
|
||||
padding: 0.75rem 1rem;
|
||||
margin: 1rem 0;
|
||||
background: #fafafa;
|
||||
border-radius: 0 8px 8px 0;
|
||||
}
|
||||
|
||||
.review-content ul,
|
||||
.review-content ol {
|
||||
padding-left: 1.5rem;
|
||||
margin: 0.5rem 0;
|
||||
}
|
||||
|
||||
.review-content li {
|
||||
margin: 0.25rem 0;
|
||||
list-style: inherit;
|
||||
}
|
||||
|
||||
/* Quill 에디터 특수 클래스 */
|
||||
.ql-editor .ql-align-center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.ql-editor .ql-align-right {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.ql-editor .ql-align-justify {
|
||||
text-align: justify;
|
||||
}
|
||||
|
||||
.ql-editor .ql-indent-1 {
|
||||
padding-left: 3em;
|
||||
}
|
||||
|
||||
.ql-editor .ql-indent-2 {
|
||||
padding-left: 6em;
|
||||
}
|
||||
|
||||
.ql-editor .ql-indent-3 {
|
||||
padding-left: 9em;
|
||||
}
|
||||
|
||||
.ql-editor .ql-size-small {
|
||||
font-size: 0.75em;
|
||||
}
|
||||
|
||||
.ql-editor .ql-size-large {
|
||||
font-size: 1.5em;
|
||||
}
|
||||
|
||||
.ql-editor .ql-size-huge {
|
||||
font-size: 2.5em;
|
||||
}
|
||||
|
||||
.ql-editor .ql-font-serif {
|
||||
font-family: Georgia, 'Times New Roman', serif;
|
||||
}
|
||||
|
||||
.ql-editor .ql-font-monospace {
|
||||
font-family: 'Monaco', 'Courier New', monospace;
|
||||
}
|
||||
|
||||
/* 목록 버튼 */
|
||||
.btn-area {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
padding-bottom: 2rem;
|
||||
}
|
||||
|
||||
.btn-list {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 0.75rem 2.5rem;
|
||||
background: white;
|
||||
color: #1a1a1a;
|
||||
border: 1px solid #e5e7eb;
|
||||
border-radius: 10px;
|
||||
font-size: 0.9375rem;
|
||||
font-weight: 600;
|
||||
text-decoration: none;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.btn-list:hover {
|
||||
border-color: #C60B24;
|
||||
color: #C60B24;
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
/* 로딩 */
|
||||
.loading {
|
||||
text-align: center;
|
||||
padding: 3rem;
|
||||
color: #6b7280;
|
||||
}
|
||||
|
||||
/* 반응형 */
|
||||
@media (max-width: 768px) {
|
||||
.container {
|
||||
padding: 1rem;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.review-header {
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
.review-content {
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
.review-title {
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
|
||||
.review-meta {
|
||||
flex-direction: column;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.container {
|
||||
padding: 0.75rem;
|
||||
}
|
||||
|
||||
.review-header {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.review-content {
|
||||
padding: 1rem;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,412 @@
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
height: 100vh;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-family: 'Pretendard', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
||||
background: #f8f9fa;
|
||||
color: #1a1a1a;
|
||||
overflow-x: hidden;
|
||||
font-size: 16px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
/* 메인 컨테이너 */
|
||||
.container {
|
||||
max-width: 1280px;
|
||||
width: 100%;
|
||||
margin: 0 auto;
|
||||
min-height: calc(100vh - 300px);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
/* 상단 헤더 */
|
||||
.header {
|
||||
background: white;
|
||||
border-radius: 16px;
|
||||
box-shadow: 0 2px 16px rgba(0, 0, 0, 0.06);
|
||||
padding: 1.5rem 2rem;
|
||||
border-bottom: 3px solid #C60B24;
|
||||
}
|
||||
|
||||
.page-title {
|
||||
font-size: clamp(1.5rem, 3vw, 1.875rem);
|
||||
font-weight: 700;
|
||||
color: #1a1a1a;
|
||||
margin: 0;
|
||||
letter-spacing: -0.025em;
|
||||
}
|
||||
|
||||
.page-subtitle {
|
||||
font-size: 0.95rem;
|
||||
color: #6b7280;
|
||||
margin: 0.25rem 0 0;
|
||||
}
|
||||
|
||||
/* 검색 영역 */
|
||||
.search-area {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.search-box {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.search-box input {
|
||||
width: 280px;
|
||||
padding: 0.625rem 1rem;
|
||||
border: 1px solid #e5e7eb;
|
||||
border-radius: 10px;
|
||||
font-size: 0.9375rem;
|
||||
outline: none;
|
||||
transition: border-color 0.2s;
|
||||
}
|
||||
|
||||
.search-box input:focus {
|
||||
border-color: #C60B24;
|
||||
}
|
||||
|
||||
.search-btn {
|
||||
padding: 0.625rem 1.25rem;
|
||||
background: #C60B24;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 10px;
|
||||
font-size: 0.9375rem;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
|
||||
.search-btn:hover {
|
||||
background: #a5091e;
|
||||
}
|
||||
|
||||
/* 시술후기 그리드 */
|
||||
.review-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(360px, 1fr));
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
/* 카드 */
|
||||
.review-card {
|
||||
background: white;
|
||||
border-radius: 16px;
|
||||
box-shadow: 0 2px 16px rgba(0, 0, 0, 0.06);
|
||||
overflow: hidden;
|
||||
transition: all 0.3s ease;
|
||||
border: 1px solid #f1f5f9;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.review-card:hover {
|
||||
transform: translateY(-4px);
|
||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.12);
|
||||
border-color: #C60B24;
|
||||
}
|
||||
|
||||
/* ===== 이미지 슬라이더 ===== */
|
||||
.review-slider {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
aspect-ratio: 4 / 3;
|
||||
overflow: hidden;
|
||||
background: #f1f5f9;
|
||||
}
|
||||
|
||||
.review-slider-track {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
transition: transform 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||
}
|
||||
|
||||
.review-slider-track img {
|
||||
min-width: 100%;
|
||||
max-width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
/* 슬라이더 화살표 */
|
||||
.slider-arrow {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
background: rgba(0, 0, 0, 0.45);
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 50%;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
opacity: 0;
|
||||
transition: opacity 0.25s;
|
||||
z-index: 2;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.review-slider:hover .slider-arrow {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.slider-arrow.prev {
|
||||
left: 8px;
|
||||
}
|
||||
|
||||
.slider-arrow.next {
|
||||
right: 8px;
|
||||
}
|
||||
|
||||
.slider-arrow:hover {
|
||||
background: rgba(0, 0, 0, 0.7);
|
||||
}
|
||||
|
||||
/* 슬라이더 도트 */
|
||||
.slider-dots {
|
||||
position: absolute;
|
||||
bottom: 8px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
display: flex;
|
||||
gap: 5px;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.slider-dot {
|
||||
width: 7px;
|
||||
height: 7px;
|
||||
border-radius: 50%;
|
||||
background: rgba(255, 255, 255, 0.5);
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
padding: 0;
|
||||
transition: all 0.25s;
|
||||
}
|
||||
|
||||
.slider-dot.active {
|
||||
background: white;
|
||||
transform: scale(1.3);
|
||||
}
|
||||
|
||||
/* 이미지 카운트 뱃지 */
|
||||
.slider-count {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
background: rgba(0, 0, 0, 0.55);
|
||||
color: white;
|
||||
font-size: 0.75rem;
|
||||
padding: 2px 8px;
|
||||
border-radius: 12px;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
/* 이미지 없을 때 */
|
||||
.review-no-image {
|
||||
width: 100%;
|
||||
aspect-ratio: 4 / 3;
|
||||
background: linear-gradient(135deg, #f1f5f9 0%, #e2e8f0 100%);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 2.5rem;
|
||||
color: #cbd5e1;
|
||||
}
|
||||
|
||||
/* 카드 바디 */
|
||||
.review-card-body {
|
||||
padding: 1.25rem 1.5rem 1.5rem;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.review-card-title {
|
||||
font-size: 1.0625rem;
|
||||
font-weight: 700;
|
||||
color: #1a1a1a;
|
||||
margin-bottom: 0.5rem;
|
||||
letter-spacing: -0.025em;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.review-card-summary {
|
||||
color: #6b7280;
|
||||
font-size: 0.8125rem;
|
||||
line-height: 1.6;
|
||||
flex: 1;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.review-card-tags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.375rem;
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.review-tag {
|
||||
display: inline-block;
|
||||
padding: 0.2rem 0.5rem;
|
||||
background: rgba(198, 11, 36, 0.08);
|
||||
color: #C60B24;
|
||||
border-radius: 20px;
|
||||
font-size: 0.6875rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.review-card-footer {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
font-size: 0.8125rem;
|
||||
color: #9ca3af;
|
||||
}
|
||||
|
||||
.review-card-footer .views {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
|
||||
/* 페이지네이션 */
|
||||
.pagination-area {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 0.375rem;
|
||||
padding: 1rem 0 2rem;
|
||||
}
|
||||
|
||||
.page-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: 1px solid #e5e7eb;
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
font-size: 0.875rem;
|
||||
color: #6b7280;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.page-btn:hover {
|
||||
border-color: #C60B24;
|
||||
color: #C60B24;
|
||||
}
|
||||
|
||||
.page-btn.active {
|
||||
background: #C60B24;
|
||||
color: white;
|
||||
border-color: #C60B24;
|
||||
}
|
||||
|
||||
.page-btn.disabled {
|
||||
opacity: 0.4;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
/* 로딩 & 빈상태 */
|
||||
.loading {
|
||||
text-align: center;
|
||||
padding: 3rem;
|
||||
color: #6b7280;
|
||||
font-size: 0.9375rem;
|
||||
grid-column: 1 / -1;
|
||||
}
|
||||
|
||||
.empty-state {
|
||||
text-align: center;
|
||||
padding: 4rem 2rem;
|
||||
grid-column: 1 / -1;
|
||||
}
|
||||
|
||||
.empty-state .icon {
|
||||
font-size: 3rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.empty-state .message {
|
||||
font-size: 1.125rem;
|
||||
color: #6b7280;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* 반응형 */
|
||||
@media (max-width: 768px) {
|
||||
.container {
|
||||
padding: 1rem;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.header {
|
||||
padding: 1rem 1.5rem;
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
.page-title {
|
||||
font-size: clamp(1.25rem, 2.5vw, 1.5rem);
|
||||
}
|
||||
|
||||
.review-grid {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.search-area {
|
||||
justify-content: stretch;
|
||||
}
|
||||
|
||||
.search-box {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.search-box input {
|
||||
flex: 1;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.slider-arrow {
|
||||
opacity: 0.7;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.container {
|
||||
padding: 0.75rem;
|
||||
}
|
||||
|
||||
.header {
|
||||
padding: 0.875rem 1rem;
|
||||
}
|
||||
|
||||
.review-card-body {
|
||||
padding: 1rem;
|
||||
}
|
||||
}
|
||||
BIN
src/main/resources/static/image/equip/20251014/울핏.jpg
Normal file
|
After Width: | Height: | Size: 99 KiB |
BIN
src/main/resources/static/image/equip/20251014/튠바디.jpg
Normal file
|
After Width: | Height: | Size: 109 KiB |
BIN
src/main/resources/static/image/equip/20260313/온다리프팅(Body).jpg
Normal file
|
After Width: | Height: | Size: 162 KiB |
BIN
src/main/resources/static/image/equip/20260313/울핏.jpg
Normal file
|
After Width: | Height: | Size: 117 KiB |
BIN
src/main/resources/static/image/equip/20260313/튠바디.jpg
Normal file
|
After Width: | Height: | Size: 131 KiB |
BIN
src/main/resources/static/image/equip/20260313/티타늄리프팅(Body).jpg
Normal file
|
After Width: | Height: | Size: 125 KiB |
BIN
src/main/resources/static/image/equip/온다리프팅.jpg
Normal file
|
After Width: | Height: | Size: 44 KiB |
BIN
src/main/resources/static/image/quick_menu/event.png
Normal file
|
After Width: | Height: | Size: 6.8 KiB |
BIN
src/main/resources/static/image/quick_menu/review.png
Normal file
|
After Width: | Height: | Size: 8.1 KiB |
BIN
src/main/resources/static/image/signature/20251024/쉬다주사.jpg
Normal file
|
After Width: | Height: | Size: 389 KiB |
598
src/main/resources/static/js/makeReservation.js
Normal file
@@ -0,0 +1,598 @@
|
||||
// 휴일(완전 휴무) 설정
|
||||
const disabledSpecificDates = [
|
||||
'2025-12-25', // 크리스마스
|
||||
'2026-01-01' // 신정
|
||||
];
|
||||
|
||||
// 단축 진료일 (15:30까지, 점심시간 없음)
|
||||
const shortWorkingDates = [
|
||||
'2025-12-24', // 크리스마스 이브
|
||||
'2025-12-31' // 연말
|
||||
];
|
||||
|
||||
// 점심시간 (14:00 ~ 15:00)
|
||||
const lunchTimeStart = 1400; // 14:00
|
||||
const lunchTimeEnd = 1500; // 15:00
|
||||
|
||||
// 날짜가 휴무일인지 확인
|
||||
function isDateDisabled(date) {
|
||||
const dateStr = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`;
|
||||
return disabledSpecificDates.includes(dateStr);
|
||||
}
|
||||
|
||||
// 단축 근무일인지 확인
|
||||
function isShortWorkingDate(date) {
|
||||
const dateStr = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`;
|
||||
return shortWorkingDates.includes(dateStr);
|
||||
}
|
||||
|
||||
// 점심시간인지 확인 (월화수목금만 해당)
|
||||
function isLunchTime(timeStr) {
|
||||
const timeNum = parseInt(timeStr.replace(':', '')); // "14:30" -> 1430
|
||||
return timeNum >= lunchTimeStart && timeNum < lunchTimeEnd;
|
||||
}
|
||||
|
||||
// 생년월일 검증 클래스
|
||||
class BirthDateValidator {
|
||||
constructor(inputId, options = {}) {
|
||||
this.inputElement = document.getElementById(inputId);
|
||||
this.messageElement = null;
|
||||
this.options = {
|
||||
showMessage: true,
|
||||
realTimeValidation: true,
|
||||
minAge: 0,
|
||||
maxAge: 150,
|
||||
format: 'YYYYMMDD',
|
||||
allowFuture: false,
|
||||
onValidationChange: null,
|
||||
...options
|
||||
};
|
||||
|
||||
if (this.inputElement && this.options.showMessage) {
|
||||
this.createMessageElement();
|
||||
}
|
||||
|
||||
if (this.inputElement && this.options.realTimeValidation) {
|
||||
this.bindEvents();
|
||||
}
|
||||
}
|
||||
|
||||
createMessageElement() {
|
||||
this.messageElement = document.createElement('div');
|
||||
this.messageElement.className = 'birth-date-message';
|
||||
this.messageElement.style.display = 'none';
|
||||
this.inputElement.parentNode.insertBefore(this.messageElement, this.inputElement.nextSibling);
|
||||
}
|
||||
|
||||
bindEvents() {
|
||||
this.inputElement.addEventListener('input', () => {
|
||||
this.inputElement.value = this.inputElement.value.replace(/[^0-9]/g, '');
|
||||
this.validateAndShowMessage();
|
||||
});
|
||||
|
||||
this.inputElement.addEventListener('blur', () => {
|
||||
this.validateAndShowMessage();
|
||||
});
|
||||
|
||||
this.inputElement.addEventListener('keydown', (e) => {
|
||||
if ([8, 9, 46, 37, 38, 39, 40].includes(e.keyCode)) return;
|
||||
if ((e.keyCode < 48 || e.keyCode > 57) && (e.keyCode < 96 || e.keyCode > 105)) {
|
||||
e.preventDefault();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
validateBirthDate(dateStr) {
|
||||
if (!dateStr) return { valid: false, message: '생년월일을 입력해주세요.' };
|
||||
|
||||
let cleanDate = dateStr.replace(/[^0-9]/g, '');
|
||||
if (cleanDate.length !== 8) {
|
||||
return { valid: false, message: '생년월일은 8자리 숫자로 입력해주세요. (예: 19900115)' };
|
||||
}
|
||||
|
||||
const year = parseInt(cleanDate.slice(0, 4));
|
||||
const month = parseInt(cleanDate.slice(4, 6));
|
||||
const day = parseInt(cleanDate.slice(6, 8));
|
||||
|
||||
const currentYear = new Date().getFullYear();
|
||||
const minYear = currentYear - this.options.maxAge;
|
||||
const maxYear = currentYear - this.options.minAge;
|
||||
|
||||
if (year < minYear || year > maxYear) {
|
||||
return { valid: false, message: `출생연도는 ${minYear}년부터 ${maxYear}년 사이여야 합니다.` };
|
||||
}
|
||||
|
||||
if (month < 1 || month > 12) return { valid: false, message: '월은 01부터 12까지 입력 가능합니다.' };
|
||||
if (day < 1 || day > 31) return { valid: false, message: '일은 01부터 31까지 입력 가능합니다.' };
|
||||
|
||||
const date = new Date(year, month - 1, day);
|
||||
const isValidDate = date.getFullYear() === year && date.getMonth() === month - 1 && date.getDate() === day;
|
||||
|
||||
if (!isValidDate) return { valid: false, message: '존재하지 않는 날짜입니다.' };
|
||||
if (!this.options.allowFuture && date > new Date()) return { valid: false, message: '미래 날짜는 입력할 수 없습니다.' };
|
||||
|
||||
return { valid: true, message: '올바른 생년월일입니다.' };
|
||||
}
|
||||
|
||||
validateAndShowMessage() {
|
||||
const result = this.validateBirthDate(this.inputElement.value);
|
||||
if (this.messageElement) {
|
||||
this.messageElement.textContent = result.message;
|
||||
this.messageElement.className = `birth-date-message ${result.valid ? 'success' : 'error'}`;
|
||||
this.messageElement.style.display = 'block';
|
||||
}
|
||||
if (typeof this.options.onValidationChange === 'function') {
|
||||
this.options.onValidationChange(result, this.inputElement.value);
|
||||
}
|
||||
return result.valid;
|
||||
}
|
||||
|
||||
isValid() {
|
||||
return this.validateBirthDate(this.inputElement.value).valid;
|
||||
}
|
||||
}
|
||||
|
||||
// 전역 변수
|
||||
let birthDateValidator;
|
||||
let selectedTreatments = [];
|
||||
let selectedDate = null;
|
||||
let selectedTime = null;
|
||||
let selectedYear, selectedMonth;
|
||||
|
||||
// DOM 요소
|
||||
const calendarTitle = document.getElementById('calendar-title');
|
||||
const calendarTable = document.getElementById('calendar-table');
|
||||
const timeSlots = document.getElementById('time-slots');
|
||||
const personCount = document.getElementById('person-count');
|
||||
const form = document.getElementById('reserve-form');
|
||||
const agree = document.getElementById('agree');
|
||||
const submitBtn = document.getElementById('submit-btn');
|
||||
const step02Title = document.getElementById('step02-title');
|
||||
const step03Title = document.getElementById('step03-title');
|
||||
|
||||
// 진료시간 설정 (점심시간 제외)
|
||||
const times_mon_wed_fri = [
|
||||
"10:00","10:30","11:00","11:30","12:00","12:30","13:00","13:30",
|
||||
"15:00","15:30","16:00","16:30","17:00","17:30","18:00","18:30"
|
||||
];
|
||||
|
||||
const times_tue_thu = [
|
||||
"10:00","10:30","11:00","11:30","12:00","12:30","13:00","13:30",
|
||||
"15:00","15:30","16:00","16:30","17:00","17:30","18:00","18:30","19:00","19:30"
|
||||
];
|
||||
|
||||
const times_sat_short = [
|
||||
"10:00","10:30","11:00","11:30","12:00","12:30","13:00","13:30",
|
||||
"14:00","14:30","15:00","15:30"
|
||||
];
|
||||
|
||||
// 시술 관리 함수들
|
||||
function removeService(el) {
|
||||
const serviceItems = document.querySelectorAll('.service-item');
|
||||
if (serviceItems.length <= 1) {
|
||||
alert('최소 1개의 시술은 선택되어 있어야 합니다.');
|
||||
return false;
|
||||
}
|
||||
|
||||
const serviceName = el.closest('.service-item').querySelector('span:first-child').textContent;
|
||||
if (!confirm(`'${serviceName}' 시술을 삭제하시겠습니까?`)) return false;
|
||||
|
||||
el.closest('.service-item').remove();
|
||||
updateTotalPrice();
|
||||
updateServiceCount();
|
||||
return true;
|
||||
}
|
||||
|
||||
function updateTotalPrice() {
|
||||
const serviceItems = document.querySelectorAll('.service-item');
|
||||
let totalPrice = 0;
|
||||
serviceItems.forEach(item => {
|
||||
const priceText = item.querySelector('.price').textContent;
|
||||
totalPrice += parseInt(priceText.replace(/[^0-9]/g, '')) || 0;
|
||||
});
|
||||
document.getElementById('total-price').textContent = totalPrice.toLocaleString() + '원';
|
||||
}
|
||||
|
||||
function updateServiceCount() {
|
||||
const serviceItems = document.querySelectorAll('.service-item');
|
||||
const count = serviceItems.length;
|
||||
const info = document.getElementById('service-count-info');
|
||||
info.textContent = `선택된 시술: ${count}개${count === 1 ? ' (최소 필수)' : ''}`;
|
||||
info.className = count === 1 ? 'service-count-info single' : 'service-count-info';
|
||||
|
||||
serviceItems.forEach(item => {
|
||||
const delBtn = item.querySelector('.del');
|
||||
if (count <= 1) {
|
||||
delBtn.className = 'del disabled';
|
||||
delBtn.title = '최소 1개의 시술은 필요합니다';
|
||||
} else {
|
||||
delBtn.className = 'del';
|
||||
delBtn.title = '삭제';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 캘린더 렌더링 (일요일 + 공휴일만 휴무)
|
||||
function renderCalendar(year, month) {
|
||||
selectedYear = year;
|
||||
selectedMonth = month;
|
||||
calendarTitle.textContent = `${year}.${String(month + 1).padStart(2, '0')}`;
|
||||
|
||||
const firstDay = new Date(year, month, 1);
|
||||
const lastDay = new Date(year, month + 1, 0);
|
||||
const todayDate = new Date();
|
||||
todayDate.setHours(0, 0, 0, 0);
|
||||
|
||||
let html = '<thead><tr>';
|
||||
['일','월','화','수','목','금','토'].forEach(d => html += `<th>${d}</th>`);
|
||||
html += '</tr></thead><tbody><tr>';
|
||||
|
||||
for(let i = 0; i < firstDay.getDay(); i++) html += '<td class="disabled"></td>';
|
||||
|
||||
for(let d = 1; d <= lastDay.getDate(); d++) {
|
||||
const dateObj = new Date(year, month, d);
|
||||
let classes = [];
|
||||
|
||||
const isPastDate = dateObj < todayDate;
|
||||
const isSunday = dateObj.getDay() === 0;
|
||||
const isHoliday = isDateDisabled(dateObj);
|
||||
|
||||
if(dateObj.toDateString() === new Date().toDateString()) classes.push('today');
|
||||
if(selectedDate && dateObj.toDateString() === selectedDate.toDateString()) classes.push('selected');
|
||||
|
||||
if(isPastDate || isSunday || isHoliday) classes.push('disabled');
|
||||
|
||||
const clickHandler = (isPastDate || isSunday || isHoliday) ?
|
||||
'' : `onclick="selectDate(${year},${month},${d})"`;
|
||||
html += `<td class="${classes.join(' ')}" ${clickHandler}>${d}</td>`;
|
||||
|
||||
if((firstDay.getDay() + d) % 7 === 0 && d !== lastDay.getDate()) html += '</tr><tr>';
|
||||
}
|
||||
|
||||
for (let i = lastDay.getDay(); i < 6; i++) {
|
||||
html += '<td class="disabled"></td>';
|
||||
}
|
||||
html += '</tr></tbody>';
|
||||
calendarTable.innerHTML = html;
|
||||
checkForm();
|
||||
}
|
||||
|
||||
function selectDate(y, m, d) {
|
||||
const tempDate = new Date(y, m, d);
|
||||
const todayDate = new Date();
|
||||
todayDate.setHours(0, 0, 0, 0);
|
||||
|
||||
if (tempDate < todayDate) {
|
||||
alert('과거 날짜는 선택할 수 없습니다.');
|
||||
return;
|
||||
}
|
||||
|
||||
if (tempDate.getDay() === 0) {
|
||||
alert('일요일은 휴무일입니다.');
|
||||
return;
|
||||
}
|
||||
|
||||
if (isDateDisabled(tempDate)) {
|
||||
alert('해당 날짜는 휴무일입니다.');
|
||||
return;
|
||||
}
|
||||
|
||||
selectedDate = tempDate;
|
||||
renderCalendar(y, m);
|
||||
renderTimeSlots();
|
||||
checkForm();
|
||||
}
|
||||
|
||||
// 월 이동
|
||||
document.getElementById('prev-month').onclick = function() {
|
||||
selectedMonth === 0 ? (selectedYear--, selectedMonth = 11) : selectedMonth--;
|
||||
renderCalendar(selectedYear, selectedMonth);
|
||||
};
|
||||
|
||||
document.getElementById('next-month').onclick = function() {
|
||||
selectedMonth === 11 ? (selectedYear++, selectedMonth = 0) : selectedMonth++;
|
||||
renderCalendar(selectedYear, selectedMonth);
|
||||
};
|
||||
|
||||
// 시간 슬롯 렌더링 (점심시간 제외 + 특수일 처리)
|
||||
function renderTimeSlots() {
|
||||
if (!selectedDate) {
|
||||
timeSlots.innerHTML = '';
|
||||
return;
|
||||
}
|
||||
|
||||
const dayOfWeek = selectedDate.getDay();
|
||||
if (dayOfWeek === 0 || isDateDisabled(selectedDate)) {
|
||||
timeSlots.innerHTML = '';
|
||||
return;
|
||||
}
|
||||
|
||||
let slotArr;
|
||||
if (isShortWorkingDate(selectedDate)) {
|
||||
slotArr = times_sat_short; // 12/24, 12/31: 토요일과 동일
|
||||
} else if ([1, 3, 5].includes(dayOfWeek)) { // 월수금
|
||||
slotArr = times_mon_wed_fri;
|
||||
} else if ([2, 4].includes(dayOfWeek)) { // 화목
|
||||
slotArr = times_tue_thu;
|
||||
} else if (dayOfWeek === 6) { // 토
|
||||
slotArr = times_sat_short;
|
||||
} else {
|
||||
timeSlots.innerHTML = '';
|
||||
return;
|
||||
}
|
||||
|
||||
const now = new Date();
|
||||
const todayDate = new Date(now.getFullYear(), now.getMonth(), now.getDate());
|
||||
const isToday = selectedDate.toDateString() === todayDate.toDateString();
|
||||
|
||||
let html = '';
|
||||
slotArr.forEach(t => {
|
||||
let isDisabled = false;
|
||||
if (isToday) {
|
||||
const currentTime = now.getHours() * 100 + now.getMinutes();
|
||||
const [hour, minute] = t.split(':').map(Number);
|
||||
if (hour * 100 + minute <= currentTime) isDisabled = true;
|
||||
}
|
||||
|
||||
const selectedClass = selectedTime === t && !isDisabled ? ' selected' : '';
|
||||
const disabledAttr = isDisabled ? 'disabled' : '';
|
||||
html += `<button type="button" class="time-btn${selectedClass}${isDisabled ? ' disabled' : ''}"
|
||||
${isDisabled ? '' : `onclick="selectTimeAndCall('${t}', this)"`} ${disabledAttr}>${t}</button>`;
|
||||
});
|
||||
|
||||
timeSlots.innerHTML = html;
|
||||
}
|
||||
|
||||
function selectTimeAndCall(t, el) {
|
||||
const now = new Date();
|
||||
const todayDate = new Date(now.getFullYear(), now.getMonth(), now.getDate());
|
||||
const isToday = selectedDate.toDateString() === todayDate.toDateString();
|
||||
|
||||
if (isToday) {
|
||||
const currentTime = now.getHours() * 100 + now.getMinutes();
|
||||
const [hour, minute] = t.split(':').map(Number);
|
||||
if (hour * 100 + minute <= currentTime) {
|
||||
alert('지난 시간은 선택할 수 없습니다.');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
selectedTime = t;
|
||||
renderTimeSlots();
|
||||
if (el) {
|
||||
document.querySelectorAll('.time-btn').forEach(btn => btn.classList.remove('active'));
|
||||
el.classList.add('active');
|
||||
}
|
||||
onClickTime(getSelectedDateStr(), t);
|
||||
checkForm();
|
||||
}
|
||||
|
||||
function getSelectedDateStr() {
|
||||
if (!selectedDate) return '';
|
||||
return selectedDate.getFullYear() +
|
||||
String(selectedDate.getMonth() + 1).padStart(2, '0') +
|
||||
String(selectedDate.getDate()).padStart(2, '0');
|
||||
}
|
||||
|
||||
// 폼 검증
|
||||
function checkForm() {
|
||||
const name = document.getElementById('customer-name').value.trim();
|
||||
const phone = document.getElementById('customer-phone').value.trim();
|
||||
const birthDate = document.getElementById('birthDate').value.trim();
|
||||
|
||||
const phoneValid = phone.match(/^01[0-9]{8,9}$/) ||
|
||||
(typeof PhoneValidator !== 'undefined' && PhoneValidator.isValid?.('customer-phone'));
|
||||
const birthDateValid = birthDate.match(/^\d{8}$/) ||
|
||||
(birthDateValidator?.isValid?.());
|
||||
|
||||
const conditions = {
|
||||
date: !!selectedDate,
|
||||
time: !!selectedTime,
|
||||
name: !!name,
|
||||
phone: !!phoneValid,
|
||||
birthDate: !!birthDateValid,
|
||||
agree: agree.checked
|
||||
};
|
||||
|
||||
const valid = Object.values(conditions).every(Boolean);
|
||||
submitBtn.disabled = !valid;
|
||||
|
||||
updateStepStatus(conditions);
|
||||
updateButtonText(conditions);
|
||||
|
||||
submitBtn.className = valid ? 'submit-btn ready' :
|
||||
selectedDate ? 'submit-btn step-progress' : 'submit-btn';
|
||||
}
|
||||
|
||||
function updateStepStatus(conditions) {
|
||||
step02Title.textContent = (conditions.date && conditions.time) ? 'STEP 02. 예약 시간 선택 ✓' : 'STEP 02. 예약 시간 선택';
|
||||
step02Title.className = (conditions.date && conditions.time) ? 'step-title completed' : 'step-title';
|
||||
|
||||
step03Title.textContent = (conditions.name && conditions.phone && conditions.birthDate && conditions.agree) ?
|
||||
'STEP 03. 고객정보 ✓' : 'STEP 03. 고객정보';
|
||||
step03Title.className = (conditions.name && conditions.phone && conditions.birthDate && conditions.agree) ?
|
||||
'step-title completed' : 'step-title';
|
||||
}
|
||||
|
||||
function updateButtonText(conditions) {
|
||||
if (!conditions.date) return submitBtn.textContent = '📅 예약 날짜를 선택해주세요';
|
||||
if (!conditions.time) return submitBtn.textContent = '⏰ 예약 시간을 선택해주세요';
|
||||
if (!conditions.name) return submitBtn.textContent = '👤 고객명을 입력해주세요';
|
||||
if (!conditions.birthDate) return submitBtn.textContent = '📅 생년월일을 올바르게 입력해주세요';
|
||||
if (!conditions.phone) return submitBtn.textContent = '📱 연락처를 올바르게 입력해주세요';
|
||||
if (!conditions.agree) return submitBtn.textContent = '✅ 개인정보 동의를 체크해주세요';
|
||||
submitBtn.textContent = '🎉 시술 예약하기';
|
||||
}
|
||||
|
||||
// 초기화
|
||||
const today = new Date();
|
||||
selectedYear = today.getFullYear();
|
||||
selectedMonth = today.getMonth();
|
||||
|
||||
form.addEventListener('input', (e) => {
|
||||
if (e.target.id !== 'customer-phone' && e.target.id !== 'birthDate') setTimeout(checkForm, 10);
|
||||
});
|
||||
agree.addEventListener('change', checkForm);
|
||||
|
||||
form.onsubmit = function(e) {
|
||||
e.preventDefault();
|
||||
if (!selectedDate || !selectedTime) {
|
||||
alert('예약 날짜와 시간을 선택해 주세요.');
|
||||
return;
|
||||
}
|
||||
if (!birthDateValidator?.isValid()) {
|
||||
alert('올바른 생년월일을 입력해 주세요.');
|
||||
return;
|
||||
}
|
||||
fn_reservation();
|
||||
};
|
||||
|
||||
// AJAX 함수들 (기존 그대로)
|
||||
function fn_reservation() {
|
||||
let formData = new FormData();
|
||||
if (selectedDate) {
|
||||
formData.append('SELECTED_DATE', `${selectedDate.getFullYear()}-${String(selectedDate.getMonth() + 1).padStart(2, '0')}-${String(selectedDate.getDate()).padStart(2, '0')}`);
|
||||
}
|
||||
if (selectedTime) formData.append('TIME', selectedTime);
|
||||
formData.append('CATEGORY_DIV_CD', typeof category_div_cd !== 'undefined' ? category_div_cd : '');
|
||||
formData.append('CATEGORY_NO', typeof category_no !== 'undefined' ? category_no : '');
|
||||
formData.append('POST_NO', typeof post_no !== 'undefined' ? post_no : '');
|
||||
formData.append('NAME', document.getElementById('customer-name').value);
|
||||
formData.append('BIRTH_DATE', document.getElementById('birthDate').value);
|
||||
formData.append('PHONE_NUMBER', document.getElementById('customer-phone').value);
|
||||
formData.append('ETC', document.getElementById('customer-req').value);
|
||||
formData.append('TREATMENT_INFOS', JSON.stringify(selectedTreatments));
|
||||
|
||||
$.ajax({
|
||||
url: encodeURI('/webservice/insertReservation.do'),
|
||||
data: formData,
|
||||
dataType: 'json',
|
||||
processData: false,
|
||||
contentType: false,
|
||||
type: 'POST',
|
||||
async: true,
|
||||
success: function(data) {
|
||||
if (data.msgCode == '0') {
|
||||
alert('예약이 완료되었습니다.');
|
||||
location.href = "/webevent/selectListWebEventIntro.do";
|
||||
} else {
|
||||
modalEvent.danger("조회 오류", data.msgDesc);
|
||||
}
|
||||
},
|
||||
error: function() {
|
||||
modalEvent.danger("조회 오류", "조회 중 오류가 발생하였습니다. 잠시후 다시시도하십시오.");
|
||||
},
|
||||
beforeSend: function() { $(".loading-image-layer").show(); },
|
||||
complete: function() { $(".loading-image-layer").hide(); }
|
||||
});
|
||||
}
|
||||
|
||||
function fn_SelectReservation(category_div_cd, category_no, post_no, procedure_id) {
|
||||
let formData = new FormData();
|
||||
formData.append('CATEGORY_DIV_CD', category_div_cd);
|
||||
formData.append('CATEGORY_NO', category_no);
|
||||
formData.append('POST_NO', post_no);
|
||||
formData.append('PROCEDURE_ID', procedure_id);
|
||||
|
||||
$.ajax({
|
||||
url: encodeURI('/webservice/selectReservation.do'),
|
||||
data: formData,
|
||||
dataType: 'json',
|
||||
processData: false,
|
||||
contentType: false,
|
||||
type: 'POST',
|
||||
async: true,
|
||||
success: function(data) {
|
||||
if (data.msgCode == '0') {
|
||||
const serviceList = document.getElementById('service-list');
|
||||
serviceList.innerHTML = '';
|
||||
let totalprice = 0;
|
||||
selectedTreatments = [];
|
||||
|
||||
if (data.reservation?.length > 0) {
|
||||
data.reservation.forEach(item => {
|
||||
let price = item.DISCOUNT_PRICE ?? item.PRICE ?? 0;
|
||||
totalprice += Number(price);
|
||||
|
||||
selectedTreatments.push({
|
||||
MU_TREATMENT_ID: item.MU_TREATMENT_ID,
|
||||
TREATMENT_NAME: item.TREATMENT_NAME,
|
||||
TREATMENT_PROCEDURE_NAME: item.TREATMENT_PROCEDURE_NAME,
|
||||
MU_TREATMENT_PROCEDURE_ID: item.MU_TREATMENT_PROCEDURE_ID
|
||||
});
|
||||
|
||||
const div = document.createElement('div');
|
||||
div.className = 'service-item';
|
||||
div.innerHTML = `
|
||||
<span>${item.TREATMENT_PROCEDURE_NAME}</span>
|
||||
<span>
|
||||
${item.DISCOUNT_PRICE != null ? `<span style="text-decoration:line-through; color:#bbb; font-size:0.95em; margin-right:6px;">${(item.PRICE || 0).toLocaleString()}원</span>` : ''}
|
||||
<span class="price">${Number(price).toLocaleString()}원</span>
|
||||
<span class="del" title="삭제" onclick="removeService(this)">×</span>
|
||||
</span>
|
||||
`;
|
||||
serviceList.appendChild(div);
|
||||
});
|
||||
}
|
||||
document.getElementById('total-price').textContent = totalprice.toLocaleString() + '원';
|
||||
updateServiceCount();
|
||||
} else {
|
||||
modalEvent.danger("조회 오류", data.msgDesc);
|
||||
}
|
||||
},
|
||||
error: function() {
|
||||
modalEvent.danger("조회 오류", "조회 중 오류가 발생하였습니다. 잠시후 다시시도하십시오.");
|
||||
},
|
||||
beforeSend: function() { $(".loading-image-layer").show(); },
|
||||
complete: function() { $(".loading-image-layer").hide(); }
|
||||
});
|
||||
}
|
||||
|
||||
function onClickTime(selectedDateStr, time) {
|
||||
let formData = new FormData();
|
||||
formData.append('SELECTED_DATE', selectedDateStr);
|
||||
formData.append('TIME', time);
|
||||
|
||||
$.ajax({
|
||||
url: encodeURI('/webservice/selectReservationCnt.do'),
|
||||
data: formData,
|
||||
dataType: 'json',
|
||||
processData: false,
|
||||
contentType: false,
|
||||
type: 'POST',
|
||||
async: true,
|
||||
success: function(data) {
|
||||
personCount.textContent = data.msgCode == '0' && data.rows?.RES_CNT !== undefined ?
|
||||
data.rows.RES_CNT : '-';
|
||||
},
|
||||
error: function() {
|
||||
personCount.textContent = '-';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 문서 준비 완료
|
||||
$(document).ready(function() {
|
||||
// Validator 초기화
|
||||
try {
|
||||
birthDateValidator = new BirthDateValidator('birthDate', {
|
||||
showMessage: true,
|
||||
realTimeValidation: true,
|
||||
onValidationChange: () => setTimeout(checkForm, 10)
|
||||
});
|
||||
} catch (e) {
|
||||
console.error('BirthDateValidator init error:', e);
|
||||
}
|
||||
|
||||
// 초기 캘린더 및 시술 로드
|
||||
const today = new Date();
|
||||
let initDate = new Date(today.getFullYear(), today.getMonth(), today.getDate());
|
||||
while (initDate.getDay() === 0 || isDateDisabled(initDate)) {
|
||||
initDate.setDate(initDate.getDate() + 1);
|
||||
}
|
||||
|
||||
renderCalendar(today.getFullYear(), today.getMonth());
|
||||
fn_SelectReservation(category_div_cd, category_no, post_no, procedure_id);
|
||||
|
||||
setTimeout(checkForm, 500);
|
||||
});
|
||||
@@ -21,10 +21,10 @@ function showBackgroundMask() {
|
||||
if (mask) {
|
||||
mask.classList.add('active');
|
||||
}
|
||||
|
||||
|
||||
// body에 popup-open 클래스 추가 (스크롤 비활성화 및 블러 효과)
|
||||
document.body.classList.add('popup-open');
|
||||
|
||||
|
||||
console.log('Background mask activated');
|
||||
}
|
||||
|
||||
@@ -34,10 +34,10 @@ function hideBackgroundMask() {
|
||||
if (mask) {
|
||||
mask.classList.remove('active');
|
||||
}
|
||||
|
||||
|
||||
// body에서 popup-open 클래스 제거
|
||||
document.body.classList.remove('popup-open');
|
||||
|
||||
|
||||
console.log('Background mask deactivated');
|
||||
}
|
||||
|
||||
@@ -50,14 +50,14 @@ const cont1Urls = [
|
||||
// 필요시 추가
|
||||
];
|
||||
|
||||
const bullet1 = ['다이어트약','지방분해주사']
|
||||
const bullet1 = ['다이어트약', '쉬다주사']
|
||||
|
||||
const useCont1Loop = bullet1.length > 2; // slidesPerView(1.3)보다 많을 때만 loop
|
||||
const cont1Swiper = new Swiper('.cont1_swiper', {
|
||||
loop: useCont1Loop,
|
||||
slidesPerView: 'auto',
|
||||
spaceBetween: 10,
|
||||
autoplay : {
|
||||
autoplay: {
|
||||
disableOnInteraction: false,
|
||||
},
|
||||
pagination: {
|
||||
@@ -69,22 +69,22 @@ const cont1Swiper = new Swiper('.cont1_swiper', {
|
||||
},
|
||||
on: {
|
||||
init: function () {
|
||||
document.querySelectorAll('.cont1_swiper .swiper-slide').forEach(function(slide, idx) {
|
||||
document.querySelectorAll('.cont1_swiper .swiper-slide').forEach(function (slide, idx) {
|
||||
slide.style.cursor = 'pointer';
|
||||
slide.setAttribute('data-url', cont1Urls[idx] || cont1Urls[0]);
|
||||
slide.addEventListener('click', function() {
|
||||
slide.addEventListener('click', function () {
|
||||
const url = slide.getAttribute('data-url');
|
||||
if(url) window.open(url, '_blank');
|
||||
if (url) window.open(url, '_blank');
|
||||
});
|
||||
});
|
||||
},
|
||||
slideChange: function () {
|
||||
document.querySelectorAll('.cont1_swiper .swiper-slide').forEach(function(slide, idx) {
|
||||
document.querySelectorAll('.cont1_swiper .swiper-slide').forEach(function (slide, idx) {
|
||||
slide.style.cursor = 'pointer';
|
||||
slide.setAttribute('data-url', cont1Urls[idx] || cont1Urls[0]);
|
||||
slide.onclick = function() {
|
||||
slide.onclick = function () {
|
||||
const url = slide.getAttribute('data-url');
|
||||
if(url) window.open(url, '_blank');
|
||||
if (url) window.open(url, '_blank');
|
||||
};
|
||||
});
|
||||
}
|
||||
@@ -95,19 +95,21 @@ const cont1Swiper = new Swiper('.cont1_swiper', {
|
||||
* content2 스와이퍼
|
||||
************************************************/
|
||||
const cont2Urls = [
|
||||
'https://diet.madeu.co.kr/webservice/selectServiceIntro.do?categoryNo=3', // 첫 번째 슬라이드 URL
|
||||
'https://diet.madeu.co.kr/webservice/selectServiceIntro.do?categoryNo=3' // 두 번째 슬라이드 URL
|
||||
'https://diet.madeu.co.kr/webservice/selectServiceDetailIntro.do?categoryDivCd=01&categoryNo=3&postNo=9',
|
||||
'https://diet.madeu.co.kr/webservice/selectServiceDetailIntro.do?categoryDivCd=01&categoryNo=3&postNo=2',
|
||||
'https://diet.madeu.co.kr/webservice/selectServiceDetailIntro.do?categoryDivCd=01&categoryNo=3&postNo=10',
|
||||
'https://diet.madeu.co.kr/webservice/selectServiceDetailIntro.do?categoryDivCd=01&categoryNo=3&postNo=1'
|
||||
// 필요시 추가
|
||||
];
|
||||
|
||||
const bullet2 = ['울핏','튠바디']
|
||||
const bullet2 = ['온다리프팅', '튠바디', '티타늄리프팅', '울핏']
|
||||
|
||||
const useCont2Loop = bullet2.length > 2; // slidesPerView(1.3)보다 많을 때만 loop
|
||||
const cont2Swiper = new Swiper('.cont2_swiper', {
|
||||
loop: useCont2Loop,
|
||||
slidesPerView: 'auto',
|
||||
spaceBetween: 10,
|
||||
autoplay : {
|
||||
autoplay: {
|
||||
disableOnInteraction: false,
|
||||
},
|
||||
pagination: {
|
||||
@@ -119,22 +121,22 @@ const cont2Swiper = new Swiper('.cont2_swiper', {
|
||||
},
|
||||
on: {
|
||||
init: function () {
|
||||
document.querySelectorAll('.cont2_swiper .swiper-slide').forEach(function(slide, idx) {
|
||||
document.querySelectorAll('.cont2_swiper .swiper-slide').forEach(function (slide, idx) {
|
||||
slide.style.cursor = 'pointer';
|
||||
slide.setAttribute('data-url', cont2Urls[idx] || cont2Urls[0]);
|
||||
slide.addEventListener('click', function() {
|
||||
slide.addEventListener('click', function () {
|
||||
const url = slide.getAttribute('data-url');
|
||||
if(url) window.open(url, '_blank');
|
||||
if (url) window.open(url, '_blank');
|
||||
});
|
||||
});
|
||||
},
|
||||
slideChange: function () {
|
||||
document.querySelectorAll('.cont2_swiper .swiper-slide').forEach(function(slide, idx) {
|
||||
document.querySelectorAll('.cont2_swiper .swiper-slide').forEach(function (slide, idx) {
|
||||
slide.style.cursor = 'pointer';
|
||||
slide.setAttribute('data-url', cont2Urls[idx] || cont2Urls[0]);
|
||||
slide.onclick = function() {
|
||||
slide.onclick = function () {
|
||||
const url = slide.getAttribute('data-url');
|
||||
if(url) window.open(url, '_blank');
|
||||
if (url) window.open(url, '_blank');
|
||||
};
|
||||
});
|
||||
}
|
||||
@@ -144,7 +146,7 @@ const cont2Swiper = new Swiper('.cont2_swiper', {
|
||||
/****************************************************************************
|
||||
* 팝업 리스트 조회 (마스킹 기능 추가)
|
||||
****************************************************************************/
|
||||
function fn_selectListWebPopupJson(){
|
||||
function fn_selectListWebPopupJson() {
|
||||
let formData = new FormData();
|
||||
|
||||
$.ajax({
|
||||
@@ -155,12 +157,12 @@ function fn_selectListWebPopupJson(){
|
||||
contentType: false,
|
||||
type: 'POST',
|
||||
async: true,
|
||||
success: function(data){
|
||||
if(data.msgCode=='0'){
|
||||
success: function (data) {
|
||||
if (data.msgCode == '0') {
|
||||
let cookiedata = document.cookie;
|
||||
if (cookiedata.indexOf("popup=done") < 0) {
|
||||
$('.popup').addClass('open');
|
||||
|
||||
|
||||
// *** 배경 마스킹 활성화 추가 ***
|
||||
showBackgroundMask();
|
||||
|
||||
@@ -168,20 +170,20 @@ function fn_selectListWebPopupJson(){
|
||||
if (0 < totalCount) {
|
||||
let popupContentList = $('#popupContentList');
|
||||
let popupTabList = $('#popupTabList');
|
||||
|
||||
popupContentList.empty();
|
||||
|
||||
popupContentList.empty();
|
||||
popupTabList.empty();
|
||||
|
||||
for (let i = 0; i < Math.min(data.rows.length, 5); i++) {
|
||||
let isActive = i === 0 ? ' active' : '';
|
||||
const isMobile = window.innerWidth <= 768;
|
||||
const imgPath = isMobile ? (data.rows[i].m_filePath || data.rows[i].filePath) : data.rows[i].filePath;
|
||||
|
||||
|
||||
// 탭 콘텐츠 생성
|
||||
let contentHTML = '';
|
||||
contentHTML += '<div role="tabpanel" class="tab-pane' + isActive + '" id="content' + (i+1) + '">';
|
||||
contentHTML += '<div role="tabpanel" class="tab-pane' + isActive + '" id="content' + (i + 1) + '">';
|
||||
contentHTML += '<a href="' + data.rows[i].url + '" target="_blank" rel="noopener">';
|
||||
contentHTML += '<img src="' + CDN_URL + imgPath + '" alt="event_con' + (i+1) + '" />';
|
||||
contentHTML += '<img src="' + CDN_URL + imgPath + '" alt="event_con' + (i + 1) + '" />';
|
||||
contentHTML += '</a>';
|
||||
contentHTML += '</div>';
|
||||
popupContentList.append(contentHTML);
|
||||
@@ -189,7 +191,7 @@ function fn_selectListWebPopupJson(){
|
||||
// 탭 리스트 생성 (data-toggle 제거하고 클릭 이벤트 직접 처리)
|
||||
let tabHTML = '';
|
||||
tabHTML += '<li role="presentation"' + (isActive ? ' class="active"' : '') + '>';
|
||||
tabHTML += '<a href="#content' + (i+1) + '" role="tab" id="contentTitle' + (i+1) + '" data-target="content' + (i+1) + '">';
|
||||
tabHTML += '<a href="#content' + (i + 1) + '" role="tab" id="contentTitle' + (i + 1) + '" data-target="content' + (i + 1) + '">';
|
||||
tabHTML += fn_addBrAfterFirstWord(data.rows[i].title);
|
||||
tabHTML += '</a>';
|
||||
tabHTML += '</li>';
|
||||
@@ -200,25 +202,25 @@ function fn_selectListWebPopupJson(){
|
||||
setupTabEvents();
|
||||
setupPopupCloseEvents();
|
||||
}
|
||||
}else{
|
||||
} else {
|
||||
$('.popup').removeClass('open');
|
||||
hideBackgroundMask(); // 추가
|
||||
}
|
||||
}else if('-2' == data.msgCode){
|
||||
modalEvent.danger("로그인 오류", data.msgDesc, function(){
|
||||
} else if ('-2' == data.msgCode) {
|
||||
modalEvent.danger("로그인 오류", data.msgDesc, function () {
|
||||
fn_leftFormAction('/weblogin/logout.do');
|
||||
});
|
||||
}else{
|
||||
} else {
|
||||
modalEvent.danger("조회 오류", data.msgDesc);
|
||||
}
|
||||
},
|
||||
error : function(xhr, status, error) {
|
||||
error: function (xhr, status, error) {
|
||||
modalEvent.danger("조회 오류", "조회 중 오류가 발생하였습니다. 잠시후 다시시도하십시오.");
|
||||
},
|
||||
beforeSend:function(){
|
||||
beforeSend: function () {
|
||||
$(".loading-image-layer").show();
|
||||
},
|
||||
complete:function(){
|
||||
complete: function () {
|
||||
$(".loading-image-layer").hide();
|
||||
}
|
||||
});
|
||||
@@ -226,15 +228,15 @@ function fn_selectListWebPopupJson(){
|
||||
|
||||
// 탭 이벤트 설정 함수 추가
|
||||
function setupTabEvents() {
|
||||
$('#popupTabList a[role="tab"]').off('click').on('click', function(e) {
|
||||
$('#popupTabList a[role="tab"]').off('click').on('click', function (e) {
|
||||
e.preventDefault();
|
||||
|
||||
|
||||
const targetId = $(this).data('target');
|
||||
|
||||
|
||||
// 모든 탭과 콘텐츠에서 active 클래스 제거
|
||||
$('#popupTabList li').removeClass('active');
|
||||
$('#popupContentList .tab-pane').removeClass('active');
|
||||
|
||||
|
||||
// 클릭된 탭과 해당 콘텐츠에 active 클래스 추가
|
||||
$(this).parent('li').addClass('active');
|
||||
$('#' + targetId).addClass('active');
|
||||
@@ -249,7 +251,7 @@ function setupPopupCloseEvents() {
|
||||
// 기존 이벤트 리스너 복제본 생성하여 교체
|
||||
const newCloseBtn = closeBtn.cloneNode(true);
|
||||
closeBtn.parentNode.replaceChild(newCloseBtn, closeBtn);
|
||||
|
||||
|
||||
// 새로운 이벤트 리스너 추가
|
||||
newCloseBtn.addEventListener('click', function () {
|
||||
//체크박스 확인
|
||||
@@ -259,7 +261,7 @@ function setupPopupCloseEvents() {
|
||||
setCookie('done', 1);
|
||||
} else {
|
||||
$('.popup').removeClass('open');
|
||||
hideBackgroundMask();
|
||||
hideBackgroundMask();
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -280,9 +282,9 @@ function fn_addBrAfterFirstWord(input) {
|
||||
function setCookie(value, expiredays) {
|
||||
let todayDate = new Date();
|
||||
todayDate.setDate(todayDate.getDate() + expiredays);
|
||||
document.cookie = "popup=" + escape( value ) + "; path=/; expires=" + todayDate.toGMTString() + ";";
|
||||
document.cookie = "popup=" + escape(value) + "; path=/; expires=" + todayDate.toGMTString() + ";";
|
||||
$('.popup').removeClass('open');
|
||||
|
||||
|
||||
// *** 배경 마스킹 제거 추가 ***
|
||||
hideBackgroundMask();
|
||||
}
|
||||
@@ -290,10 +292,10 @@ function setCookie(value, expiredays) {
|
||||
/****************************************************************************
|
||||
* 메인 배너 리스트 조회
|
||||
****************************************************************************/
|
||||
function fn_selectListWebMainBannerTypeAJson(){
|
||||
function fn_selectListWebMainBannerTypeAJson() {
|
||||
let formData = new FormData();
|
||||
formData.append('bannerType', 'DT');
|
||||
|
||||
|
||||
$.ajax({
|
||||
url: encodeURI('/webmainbanner/selectListWebMainBanner.do'),
|
||||
data: formData,
|
||||
@@ -302,8 +304,8 @@ function fn_selectListWebMainBannerTypeAJson(){
|
||||
contentType: false,
|
||||
type: 'POST',
|
||||
async: true,
|
||||
success: function(data){
|
||||
if(data.msgCode=='0'){
|
||||
success: function (data) {
|
||||
if (data.msgCode == '0') {
|
||||
let totalCount = data.rows.length;
|
||||
if (0 < totalCount) {
|
||||
let listHTML = '';
|
||||
@@ -316,8 +318,8 @@ function fn_selectListWebMainBannerTypeAJson(){
|
||||
// 이미지와 링크를 a 태그로 감싸기 (url 파라미터 사용)
|
||||
listHTML += '<div class="swiper-slide" style="background:' + backgroundColor + ';" aria-label="' + currentAriaLabel + '">';
|
||||
listHTML += ' <a href="' + data.rows[i].url + '" target="_blank" rel="noopener">';
|
||||
listHTML += ' <img class="pc" src="' + CDN_URL + data.rows[i].webFilePath + '" alt="banner' + (i+1) + '" />';
|
||||
listHTML += ' <img class="mb" src="' + CDN_URL + data.rows[i].mobileFilePath + '" alt="banner' + (i+1) + '" />';
|
||||
listHTML += ' <img class="pc" src="' + CDN_URL + data.rows[i].webFilePath + '" alt="banner' + (i + 1) + '" />';
|
||||
listHTML += ' <img class="mb" src="' + CDN_URL + data.rows[i].mobileFilePath + '" alt="banner' + (i + 1) + '" />';
|
||||
listHTML += ' </a>';
|
||||
listHTML += '</div>';
|
||||
}
|
||||
@@ -330,7 +332,7 @@ function fn_selectListWebMainBannerTypeAJson(){
|
||||
const mainBannerSwiper = new Swiper('.main_banner_swiper', {
|
||||
loop: useMainBannerLoop,
|
||||
slidesPerView: 1,
|
||||
autoplay : {
|
||||
autoplay: {
|
||||
disableOnInteraction: false,
|
||||
},
|
||||
pagination: {
|
||||
@@ -351,7 +353,7 @@ function fn_selectListWebMainBannerTypeAJson(){
|
||||
},
|
||||
});
|
||||
|
||||
///////// swiper-pagination-bullet 넓이 동적 조정 /////////
|
||||
///////// swiper-pagination-bullet 넓이 동적 조정 /////////
|
||||
function adjustBulletWidth() {
|
||||
const bullets = document.querySelectorAll('.main_banner_pagination .swiper-pagination-bullet');
|
||||
const paginationWidth = 490;
|
||||
@@ -363,43 +365,43 @@ function fn_selectListWebMainBannerTypeAJson(){
|
||||
}
|
||||
|
||||
///////// 활성화된 swiper-pagination-bullet 업데이트 /////////
|
||||
function updateActiveBullets(activeIndex) {
|
||||
const bullets = document.querySelectorAll('.mainbannerpagination .swiper-pagination-bullet');
|
||||
bullets.forEach(bullet => bullet.classList.remove('swiper-pagination-bullet-active')); // 먼저 모두 제거
|
||||
if (bullets[activeIndex]) {
|
||||
bullets[activeIndex].classList.add('swiper-pagination-bullet-active'); // 하나만 추가
|
||||
}
|
||||
}
|
||||
function updateActiveBullets(activeIndex) {
|
||||
const bullets = document.querySelectorAll('.mainbannerpagination .swiper-pagination-bullet');
|
||||
bullets.forEach(bullet => bullet.classList.remove('swiper-pagination-bullet-active')); // 먼저 모두 제거
|
||||
if (bullets[activeIndex]) {
|
||||
bullets[activeIndex].classList.add('swiper-pagination-bullet-active'); // 하나만 추가
|
||||
}
|
||||
}
|
||||
|
||||
}else{$("#mainBannerList").empty();}
|
||||
}else if('-2' == data.msgCode){
|
||||
modalEvent.danger("로그인 오류", data.msgDesc, function(){
|
||||
} else { $("#mainBannerList").empty(); }
|
||||
} else if ('-2' == data.msgCode) {
|
||||
modalEvent.danger("로그인 오류", data.msgDesc, function () {
|
||||
fn_leftFormAction('/weblogin/logout.do');
|
||||
});
|
||||
}else{
|
||||
} else {
|
||||
modalEvent.danger("조회 오류", data.msgDesc);
|
||||
}
|
||||
|
||||
},
|
||||
error : function(xhr, status, error) {
|
||||
error: function (xhr, status, error) {
|
||||
modalEvent.danger("조회 오류", "조회 중 오류가 발생하였습니다. 잠시후 다시시도하십시오.");
|
||||
},
|
||||
beforeSend:function(){
|
||||
beforeSend: function () {
|
||||
// 로딩열기
|
||||
$(".loading-image-layer").show();
|
||||
},
|
||||
complete:function(){
|
||||
complete: function () {
|
||||
// 로딩닫기
|
||||
$(".loading-image-layer").hide();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* 서브 배너 이벤트 리스트 조회
|
||||
****************************************************************************/
|
||||
function fn_selectListWebMainBannerTypeBJson(){
|
||||
function fn_selectListWebMainBannerTypeBJson() {
|
||||
let formData = new FormData();
|
||||
formData.append('bannerType', 'DB');
|
||||
|
||||
@@ -411,8 +413,8 @@ function fn_selectListWebMainBannerTypeBJson(){
|
||||
contentType: false,
|
||||
type: 'POST',
|
||||
async: true,
|
||||
success: function(data){
|
||||
if(data.msgCode=='0'){
|
||||
success: function (data) {
|
||||
if (data.msgCode == '0') {
|
||||
let totalCount = data.rows.length;
|
||||
|
||||
if (0 < totalCount) {
|
||||
@@ -426,8 +428,8 @@ function fn_selectListWebMainBannerTypeBJson(){
|
||||
// 이미지와 링크를 a 태그로 감싸기 (url 파라미터 사용)
|
||||
listHTML += '<div class="swiper-slide" style="background:' + backgroundColor + ';" aria-label="' + currentAriaLabel + '">';
|
||||
listHTML += ' <a href="' + data.rows[i].url + '" target="_blank" rel="noopener">';
|
||||
listHTML += ' <img class="pc" src="' + CDN_URL + data.rows[i].webFilePath + '" alt="banner' + (i+1) + '" />';
|
||||
listHTML += ' <img class="mb" src="' + CDN_URL + data.rows[i].mobileFilePath + '" alt="banner' + (i+1) + '" />';
|
||||
listHTML += ' <img class="pc" src="' + CDN_URL + data.rows[i].webFilePath + '" alt="banner' + (i + 1) + '" />';
|
||||
listHTML += ' <img class="mb" src="' + CDN_URL + data.rows[i].mobileFilePath + '" alt="banner' + (i + 1) + '" />';
|
||||
listHTML += ' <button class="detail_btn">Detail view ></button>';
|
||||
listHTML += ' </a>';
|
||||
listHTML += '</div>';
|
||||
@@ -441,7 +443,7 @@ function fn_selectListWebMainBannerTypeBJson(){
|
||||
const subBannerSwiper = new Swiper('.sub_banner_swiper', {
|
||||
loop: useSubBannerLoop,
|
||||
slidesPerView: 1,
|
||||
autoplay : {
|
||||
autoplay: {
|
||||
disableOnInteraction: false,
|
||||
},
|
||||
pagination: {
|
||||
@@ -462,7 +464,7 @@ function fn_selectListWebMainBannerTypeBJson(){
|
||||
},
|
||||
});
|
||||
|
||||
///////// swiper-pagination-bullet 넓이 동적 조정 /////////
|
||||
///////// swiper-pagination-bullet 넓이 동적 조정 /////////
|
||||
function adjustSubBulletWidth() {
|
||||
const bullets = document.querySelectorAll('.sub_banner_pagination .swiper-pagination-bullet');
|
||||
const paginationWidth = 490;
|
||||
@@ -486,24 +488,24 @@ function fn_selectListWebMainBannerTypeBJson(){
|
||||
});
|
||||
}
|
||||
|
||||
}else{$("#subBannerList").empty();}
|
||||
}else if('-2' == data.msgCode){
|
||||
modalEvent.danger("로그인 오류", data.msgDesc, function(){
|
||||
} else { $("#subBannerList").empty(); }
|
||||
} else if ('-2' == data.msgCode) {
|
||||
modalEvent.danger("로그인 오류", data.msgDesc, function () {
|
||||
fn_leftFormAction('/weblogin/logout.do');
|
||||
});
|
||||
}else{
|
||||
} else {
|
||||
modalEvent.danger("조회 오류", data.msgDesc);
|
||||
}
|
||||
|
||||
},
|
||||
error : function(xhr, status, error) {
|
||||
error: function (xhr, status, error) {
|
||||
modalEvent.danger("조회 오류", "조회 중 오류가 발생하였습니다. 잠시후 다시시도하십시오.");
|
||||
},
|
||||
beforeSend:function(){
|
||||
beforeSend: function () {
|
||||
// 로딩열기
|
||||
$(".loading-image-layer").show();
|
||||
},
|
||||
complete:function(){
|
||||
complete: function () {
|
||||
// 로딩닫기
|
||||
$(".loading-image-layer").hide();
|
||||
}
|
||||
@@ -513,7 +515,7 @@ function fn_selectListWebMainBannerTypeBJson(){
|
||||
/****************************************************************************
|
||||
* 인스타그램 피드 리스트 조회 (백엔드 API 사용)
|
||||
****************************************************************************/
|
||||
function fn_selectListWebInstagramJson(){
|
||||
function fn_selectListWebInstagramJson() {
|
||||
let formData = new FormData();
|
||||
$.ajax({
|
||||
url: encodeURI('/webinstagram/selectListWebInstagram.do'),
|
||||
@@ -523,8 +525,8 @@ function fn_selectListWebInstagramJson(){
|
||||
contentType: false,
|
||||
type: 'POST',
|
||||
async: true,
|
||||
success: function(data){
|
||||
if(data.msgCode=='0'){
|
||||
success: function (data) {
|
||||
if (data.msgCode == '0') {
|
||||
let totalCount = data.rows.length;
|
||||
if (0 < totalCount) {
|
||||
let listHTML = '';
|
||||
@@ -535,12 +537,12 @@ function fn_selectListWebInstagramJson(){
|
||||
let permalink = data.rows[i].permalink;
|
||||
let mediaType = data.rows[i].media_type || '';
|
||||
let thumbnailUrl = data.rows[i].thumbnail_url || mediaUrl;
|
||||
listHTML += '<div class="swiper-slide" aria-label="'+currentAriaLabel+'">';
|
||||
listHTML += ' <a href="'+permalink+'" target="_blank" rel="noopener">';
|
||||
listHTML += '<div class="swiper-slide" aria-label="' + currentAriaLabel + '">';
|
||||
listHTML += ' <a href="' + permalink + '" target="_blank" rel="noopener">';
|
||||
if (mediaType === 'VIDEO' || mediaType === 'REEL') {
|
||||
listHTML += ' <img src="'+thumbnailUrl+'" alt="instagram'+(i+1)+'" />';
|
||||
listHTML += ' <img src="' + thumbnailUrl + '" alt="instagram' + (i + 1) + '" />';
|
||||
} else {
|
||||
listHTML += ' <img src="'+mediaUrl+'" alt="instagram'+(i+1)+'" />';
|
||||
listHTML += ' <img src="' + mediaUrl + '" alt="instagram' + (i + 1) + '" />';
|
||||
}
|
||||
listHTML += ' </a>';
|
||||
listHTML += '</div>';
|
||||
@@ -549,22 +551,22 @@ function fn_selectListWebInstagramJson(){
|
||||
// 커스텀 내비게이션과 연동
|
||||
initInstagramCustomNav(data);
|
||||
}
|
||||
}else if('-2' == data.msgCode){
|
||||
modalEvent.danger("로그인 오류", data.msgDesc, function(){
|
||||
} else if ('-2' == data.msgCode) {
|
||||
modalEvent.danger("로그인 오류", data.msgDesc, function () {
|
||||
fn_leftFormAction('/weblogin/logout.do');
|
||||
});
|
||||
}else{
|
||||
} else {
|
||||
modalEvent.danger("조회 오류", data.msgDesc);
|
||||
}
|
||||
},
|
||||
error : function(xhr, status, error) {
|
||||
error: function (xhr, status, error) {
|
||||
modalEvent.danger("조회 오류", "조회 중 오류가 발생하였습니다. 잠시후 다시시도하십시오.");
|
||||
},
|
||||
beforeSend:function(){
|
||||
beforeSend: function () {
|
||||
// 로딩열기
|
||||
$(".loading-image-layer").show();
|
||||
},
|
||||
complete:function(){
|
||||
complete: function () {
|
||||
// 로딩닫기
|
||||
$(".loading-image-layer").hide();
|
||||
}
|
||||
@@ -577,12 +579,12 @@ function fn_selectListWebInstagramJson(){
|
||||
function initInstagramCustomNav(data) {
|
||||
const totalCount = data.rows.length;
|
||||
if (totalCount === 0) return;
|
||||
|
||||
|
||||
// 썸네일은 최대 3개로 고정
|
||||
const maxThumbs = 3;
|
||||
const showThumbs = Math.min(totalCount, maxThumbs);
|
||||
let currentActiveIndex = 0; // 현재 활성 인덱스 추적
|
||||
|
||||
|
||||
// 썸네일 버튼 생성 (활성 상태 포함)
|
||||
function createThumbnails(startIndex = 0) {
|
||||
let thumbsHTML = '';
|
||||
@@ -592,17 +594,17 @@ function initInstagramCustomNav(data) {
|
||||
const thumbnailUrl = data.rows[dataIndex].thumbnail_url || mediaUrl;
|
||||
const isActive = dataIndex === currentActiveIndex ? 'active' : '';
|
||||
thumbsHTML += `<div class="custom-swiper-thumb ${isActive}" data-index="${dataIndex}">
|
||||
<img src="${thumbnailUrl}" alt="thumb${dataIndex+1}" />
|
||||
<img src="${thumbnailUrl}" alt="thumb${dataIndex + 1}" />
|
||||
</div>`;
|
||||
}
|
||||
document.querySelector('.custom-swiper-thumbs').innerHTML = thumbsHTML;
|
||||
}
|
||||
|
||||
|
||||
// 초기 썸네일 생성
|
||||
createThumbnails(0);
|
||||
|
||||
// Swiper 인스턴스
|
||||
setTimeout(function() {
|
||||
setTimeout(function () {
|
||||
const instagramSwiper = new Swiper('.instagram_swiper', {
|
||||
loop: true,
|
||||
autoplay: {
|
||||
@@ -619,18 +621,18 @@ function initInstagramCustomNav(data) {
|
||||
},
|
||||
},
|
||||
on: {
|
||||
slideChange: function() {
|
||||
slideChange: function () {
|
||||
const newIndex = this.realIndex % totalCount;
|
||||
currentActiveIndex = newIndex; // 활성 인덱스 업데이트
|
||||
|
||||
|
||||
// 콘텐츠가 3개보다 많은 경우 썸네일 순환 업데이트
|
||||
if (totalCount > maxThumbs) {
|
||||
// 현재 활성 썸네일이 보이는지 확인
|
||||
const visibleThumbs = Array.from(document.querySelectorAll('.custom-swiper-thumb'));
|
||||
const isActiveVisible = visibleThumbs.some(thumb =>
|
||||
const isActiveVisible = visibleThumbs.some(thumb =>
|
||||
parseInt(thumb.dataset.index) === currentActiveIndex
|
||||
);
|
||||
|
||||
|
||||
// 활성 썸네일이 보이지 않으면 썸네일 업데이트
|
||||
if (!isActiveVisible) {
|
||||
updateThumbnailsForIndex(currentActiveIndex);
|
||||
@@ -646,41 +648,41 @@ function initInstagramCustomNav(data) {
|
||||
});
|
||||
|
||||
// 좌우 버튼 이벤트
|
||||
document.querySelector('.custom-swiper-btn.prev').onclick = function() {
|
||||
document.querySelector('.custom-swiper-btn.prev').onclick = function () {
|
||||
instagramSwiper.slidePrev();
|
||||
};
|
||||
document.querySelector('.custom-swiper-btn.next').onclick = function() {
|
||||
document.querySelector('.custom-swiper-btn.next').onclick = function () {
|
||||
instagramSwiper.slideNext();
|
||||
};
|
||||
|
||||
|
||||
// 썸네일 클릭 이벤트
|
||||
document.querySelector('.custom-swiper-thumbs').onclick = function(e) {
|
||||
document.querySelector('.custom-swiper-thumbs').onclick = function (e) {
|
||||
const thumb = e.target.closest('.custom-swiper-thumb');
|
||||
if (thumb) {
|
||||
const targetIndex = parseInt(thumb.dataset.index);
|
||||
instagramSwiper.slideToLoop(targetIndex);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// 현재 슬라이드에 맞춰 썸네일 업데이트
|
||||
function updateThumbnailsForIndex(activeIndex) {
|
||||
if (totalCount <= maxThumbs) return;
|
||||
|
||||
|
||||
// 현재 활성 인덱스를 중심으로 썸네일 배치
|
||||
let startIndex = activeIndex - Math.floor(maxThumbs / 2);
|
||||
if (startIndex < 0) startIndex = totalCount + startIndex;
|
||||
|
||||
|
||||
createThumbnails(startIndex);
|
||||
}
|
||||
|
||||
|
||||
// 활성 썸네일 표시
|
||||
function updateCustomThumbActive(activeIdx) {
|
||||
document.querySelectorAll('.custom-swiper-thumb').forEach(function(thumb) {
|
||||
document.querySelectorAll('.custom-swiper-thumb').forEach(function (thumb) {
|
||||
const thumbIndex = parseInt(thumb.dataset.index);
|
||||
thumb.classList.toggle('active', thumbIndex === activeIdx);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// 초기 활성 상태 설정
|
||||
updateCustomThumbActive(0);
|
||||
}, 0);
|
||||
@@ -690,29 +692,29 @@ function initInstagramCustomNav(data) {
|
||||
fn_init();
|
||||
|
||||
// 기존 팝업 닫기 버튼 이벤트 (마스킹 기능 추가)
|
||||
document.getElementById('btnPopupClose').addEventListener('click',function (){
|
||||
document.getElementById('btnPopupClose').addEventListener('click', function () {
|
||||
$('.popup').removeClass('open');
|
||||
// *** 배경 마스킹 제거 추가 ***
|
||||
hideBackgroundMask();
|
||||
});
|
||||
|
||||
// 추가 이벤트 리스너들 (새로 추가)
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
// ESC 키로 팝업 닫기
|
||||
document.addEventListener('keydown', function(e) {
|
||||
document.addEventListener('keydown', function (e) {
|
||||
if (e.key === 'Escape' && document.querySelector('.popup.open')) {
|
||||
$('.popup').removeClass('open');
|
||||
hideBackgroundMask();
|
||||
}
|
||||
});
|
||||
// 마스크 영역 클릭 시 팝업 닫기 (선택사항)
|
||||
// 마스크 영역 클릭 시 팝업 닫기 (선택사항)
|
||||
const mask = document.querySelector('.popup-background-mask');
|
||||
if (mask) {
|
||||
mask.addEventListener('click', function() {
|
||||
mask.addEventListener('click', function () {
|
||||
$('.popup').removeClass('open');
|
||||
hideBackgroundMask();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
});
|
||||
|
||||
@@ -188,16 +188,17 @@ function fn_RenderServices(services) {
|
||||
}
|
||||
|
||||
let servicesHtml = '';
|
||||
services.forEach(function(service) {
|
||||
|
||||
servicesHtml += `
|
||||
<div class="service-card" onclick="fn_GoToDetail('${service.CATEGORY_DIV_CD}', '${service.CATEGORY_NO}', '${service.POST_NO}')" style="cursor: pointer;">
|
||||
<div class="service-title">${service.SERVICE_NM || service.TITLE || '서비스명 없음'}</div>
|
||||
<div class="service-description">${service.SERVICE_DESC || service.CONTENT || '설명 없음'}</div>
|
||||
<div class="service-price"><span class="cancel-price">${fn_FormatPrice(service.PRICE)}</span> ${fn_FormatPrice(service.DISCOUNT_PRICE)}</div>
|
||||
</div>
|
||||
`;
|
||||
});
|
||||
services.forEach(function(service) {
|
||||
servicesHtml += `
|
||||
<div class="service-card" onclick="fn_GoToDetail('${service.CATEGORY_DIV_CD}', '${service.CATEGORY_NO}', '${service.POST_NO}')" style="cursor: pointer;">
|
||||
<div class="service-title">${service.SERVICE_NM || service.TITLE || '서비스명 없음'}</div>
|
||||
<div class="service-description">${service.SERVICE_DESC || service.CONTENT || '설명 없음'}</div>
|
||||
<div class="service-price">
|
||||
${service.PRICE !== service.DISCOUNT_PRICE ? `<span class="cancel-price">${fn_FormatPrice(service.PRICE)}</span> ` : ''}${fn_FormatPrice(service.DISCOUNT_PRICE)}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
});
|
||||
|
||||
servicesGrid.innerHTML = servicesHtml;
|
||||
}
|
||||
|
||||
@@ -1,236 +1,314 @@
|
||||
/************************************************
|
||||
* 초기화
|
||||
************************************************/
|
||||
function fn_init() {
|
||||
fn_SelectDetail(category_div_cd, category_no, post_no);
|
||||
|
||||
$("#btn_makeReservation").on("click", function(){
|
||||
fn_moveReservation(category_div_cd, category_no, post_no);
|
||||
});
|
||||
}
|
||||
// 전역 변수
|
||||
let procedureChoices;
|
||||
let priceList = [];
|
||||
const totalEl = document.getElementById('total');
|
||||
const reserveBtn = document.getElementById('reserve-btn');
|
||||
|
||||
// 초기화
|
||||
fn_SelectDetail(category_div_cd, category_no, post_no);
|
||||
|
||||
/****************************************************************************
|
||||
* 카테고리 목록 가져오기
|
||||
****************************************************************************/
|
||||
function fn_SelectDetail(category_div_cd, category_no, post_no){
|
||||
|
||||
function fn_SelectDetail(category_div_cd, category_no, post_no) {
|
||||
let formData = new FormData();
|
||||
formData.append('CATEGORY_DIV_CD', category_div_cd);
|
||||
formData.append('CATEGORY_DIV_CD', category_div_cd);
|
||||
formData.append('CATEGORY_NO', category_no);
|
||||
formData.append('POST_NO', post_no);
|
||||
|
||||
$.ajax({
|
||||
url: encodeURI('/webevent/selectEventDetail.do'),
|
||||
data: formData,
|
||||
dataType: 'json',
|
||||
processData: false,
|
||||
contentType: false,
|
||||
type: 'POST',
|
||||
async: true,
|
||||
success: function(data){
|
||||
if(data.msgCode=='0'){
|
||||
//화면 데이터 변경
|
||||
$('#category_nm').text(data.rows.CATEGORY_NM);
|
||||
$.ajax({
|
||||
url: encodeURI('/webevent/selectEventDetail.do'),
|
||||
data: formData,
|
||||
dataType: 'json',
|
||||
processData: false,
|
||||
contentType: false,
|
||||
type: 'POST',
|
||||
async: true,
|
||||
success: function (data) {
|
||||
if (data.msgCode == '0') {
|
||||
// 화면 데이터 변경
|
||||
$('#title').text(data.rows.TITLE);
|
||||
$('#serviceThumb').attr('src', data.rows.THUMBNAIL_PATH);
|
||||
$('#main_title').text(data.rows.TITLE);
|
||||
$('.thumbnail-bottom-txt').text(data.rows.THUMBNAIL_BOTTOM_TXT);
|
||||
$('#serviceThumb').attr('src', CDN_URL + data.rows.THUMBNAIL_PATH);
|
||||
$('#thumbnail-bottom-txt').text(data.rows.THUMBNAIL_BOTTOM_TXT);
|
||||
$('#contents').text(data.rows.CONTENT);
|
||||
if(data.rows.PRICE == null || data.rows.PRICE == undefined){
|
||||
$('#startprice').text('0');
|
||||
}else{
|
||||
$('#startprice').text(data.rows.PRICE.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ","));
|
||||
}
|
||||
$('#hashtag').text(data.rows.HASHTAG);
|
||||
$('#contents_path').text(data.rows.THUMBNAIL_BOTTOM_TXT);
|
||||
$('#contents_path').attr('src', data.rows.CONTENTS_PATH);
|
||||
//시술 목록 데이터 입력
|
||||
let totalCount = data.price.length;
|
||||
if (0 < totalCount) {
|
||||
for (let i = 0; i < data.price.length; i++) {
|
||||
let listHTML = '';
|
||||
listHTML += '<li class="optipon_item sepr_wrap">';
|
||||
listHTML += ' <div class="item_subprice">';
|
||||
listHTML += ' <div class="cs-checkbox">';
|
||||
listHTML += ' <input type="checkbox" class="idxChk" id="checkboxID' + i + '">';
|
||||
listHTML += ' <label class="cs-checkbox-label d-block" for="checkboxID' + i + '">';
|
||||
listHTML += ' <div class="oi-wrap">';
|
||||
listHTML += ' <div class="oi-txt">';
|
||||
listHTML += ' <span class="oi-tit-txt">' + data.price[i].TREATMENT_PROCEDURE_NAME;
|
||||
listHTML += ' </span>';
|
||||
listHTML += ' </div>';
|
||||
listHTML += ' <div class="oi-price">';
|
||||
listHTML += ' <del class="original_price d-block">';
|
||||
listHTML += ' <b>';
|
||||
if(data.price[i].DISCOUNT_PRICE == null || data.price[i].DISCOUNT_PRICE == undefined){
|
||||
if(data.price[i].PRICE == null || data.price[i].PRICE == undefined){
|
||||
listHTML += '0';
|
||||
}else{
|
||||
listHTML += (data.price[i].PRICE).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
|
||||
}
|
||||
}else{
|
||||
listHTML += (data.price[i].DISCOUNT_PRICE).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
|
||||
}
|
||||
listHTML += '</b>원';
|
||||
listHTML += ' </del>';
|
||||
listHTML += ' <span class="discount_price">';
|
||||
if(data.price[i].DISCOUNT_PRICE == null || data.price[i].DISCOUNT_PRICE == undefined){
|
||||
if(data.price[i].PRICE == null || data.price[i].PRICE == undefined){
|
||||
listHTML += '0';
|
||||
}else{
|
||||
listHTML += (data.price[i].PRICE).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
|
||||
}
|
||||
}else{
|
||||
listHTML += (data.price[i].DISCOUNT_PRICE).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
|
||||
}
|
||||
listHTML += '</span>원';
|
||||
listHTML += ' </div>';
|
||||
listHTML += ' </div>';
|
||||
listHTML += ' </label>';
|
||||
listHTML += ' </div>';
|
||||
listHTML += ' </div>';
|
||||
listHTML += '</li>';
|
||||
$(".option_list").append(listHTML);
|
||||
|
||||
$("#checkboxID" + i).change(function(){
|
||||
if($("#checkboxID" + i).is(":checked")){
|
||||
let listHTML2 = '';
|
||||
listHTML2 += '<li class="s_item selt_info_wrap" id="liid' + i + '">';
|
||||
listHTML2 += '<input type="hidden" id="procedure_id' + i + '" name="procedure_id" value="'+ data.price[i].MU_TREATMENT_PROCEDURE_ID + '">';
|
||||
listHTML2 += ' <div class="txt-wrap"> ';
|
||||
listHTML2 += ' <span class="selt">' + data.price[i].TREATMENT_PROCEDURE_NAME;
|
||||
listHTML2 += ' </div>';
|
||||
listHTML2 += ' <div class="info">';
|
||||
listHTML2 += ' <span><span class="real_price">';
|
||||
if(data.price[i].DISCOUNT_PRICE == null || data.price[i].DISCOUNT_PRICE == undefined){
|
||||
if(data.price[i].PRICE == null || data.price[i].PRICE == undefined){
|
||||
listHTML2 += '0';
|
||||
}else{
|
||||
listHTML2 += (data.price[i].PRICE).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
|
||||
}
|
||||
}else{
|
||||
listHTML2 += (data.price[i].DISCOUNT_PRICE).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
|
||||
}
|
||||
listHTML2 += '</span>원</span>';
|
||||
listHTML2 += ' <button type="button" onclick="fn_nocheck(' + i + ');"><img src="/image/close-btn.png" alt="삭제" width="13px" height="13px"></button>';
|
||||
listHTML2 += ' </div>';
|
||||
listHTML2 += '</li>';
|
||||
$("#selectEvent1").append(listHTML2);
|
||||
var price = $("#price_div1").text().replace(/,/g, "");
|
||||
var discount_price = '';
|
||||
if(data.price[i].DISCOUNT_PRICE == null || data.price[i].DISCOUNT_PRICE == undefined){
|
||||
if(data.price[i].PRICE == null || data.price[i].PRICE == undefined){
|
||||
discount_price = 0;
|
||||
}else{
|
||||
discount_price = data.price[i].PRICE;
|
||||
}
|
||||
}else{
|
||||
discount_price = data.price[i].DISCOUNT_PRICE;
|
||||
}
|
||||
price = Number(price) + Number(discount_price);
|
||||
$("#price_div1").text(price.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ","));
|
||||
}else{
|
||||
fn_deleteList(i);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
}else{
|
||||
listHTML += '<li>';
|
||||
listHTML += '</li>';
|
||||
$(".option_list").html(listHTML);
|
||||
|
||||
if (data.rows.PRICE == null || data.rows.PRICE == undefined) {
|
||||
$('#price').text('0');
|
||||
} else {
|
||||
$('#price').text(data.rows.PRICE.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ","));
|
||||
}
|
||||
}else{
|
||||
modalEvent.danger("조회 오류", data.msgDesc);
|
||||
}
|
||||
|
||||
},
|
||||
error : function(xhr, status, error) {
|
||||
modalEvent.danger("조회 오류", "조회 중 오류가 발생하였습니다. 잠시후 다시시도하십시오.");
|
||||
},
|
||||
beforeSend:function(){
|
||||
// 로딩열기
|
||||
$(".loading-image-layer").show();
|
||||
},
|
||||
complete:function(){
|
||||
// 로딩닫기
|
||||
$(".loading-image-layer").hide();
|
||||
}
|
||||
});
|
||||
// header 동적 변경
|
||||
$('#header-category-nm').text(data.rows.CATEGORY_NM);
|
||||
$('#header-title').text(data.rows.TITLE);
|
||||
|
||||
// 해시태그 처리
|
||||
var hashtagHtml = '';
|
||||
if (data.rows.HASHTAG) {
|
||||
var tags = data.rows.HASHTAG.split('#');
|
||||
tags.forEach(function (tag) {
|
||||
var trimmed = tag.trim();
|
||||
if (trimmed) {
|
||||
hashtagHtml += '<span class="hashtag">#' + trimmed + '</span>';
|
||||
}
|
||||
});
|
||||
}
|
||||
$('.hashtag-list').html(hashtagHtml);
|
||||
|
||||
$('#contents_path').attr('src', CDN_URL + data.rows.CONTENTS_PATH);
|
||||
|
||||
// 이벤트 기간 정보 저장
|
||||
window.eventStartDt = data.rows.EVENT_START_DT || '';
|
||||
window.eventEndDt = data.rows.EVENT_END_DT || '';
|
||||
|
||||
// 이벤트 기간 표시
|
||||
if (window.eventStartDt || window.eventEndDt) {
|
||||
const periodEl = document.getElementById('event-period');
|
||||
if (periodEl) {
|
||||
let periodText = '📅 이벤트 기간: ';
|
||||
if (window.eventStartDt && window.eventEndDt) {
|
||||
periodText += window.eventStartDt + ' ~ ' + window.eventEndDt;
|
||||
} else if (window.eventEndDt) {
|
||||
periodText += '~ ' + window.eventEndDt;
|
||||
} else {
|
||||
periodText += window.eventStartDt + ' ~';
|
||||
}
|
||||
periodEl.textContent = periodText;
|
||||
periodEl.style.display = 'block';
|
||||
}
|
||||
}
|
||||
|
||||
// 시술 목록 데이터 처리
|
||||
updateProcedureOptions(data.price || []);
|
||||
} else {
|
||||
modalEvent.danger("조회 오류", data.msgDesc);
|
||||
}
|
||||
},
|
||||
error: function (xhr, status, error) {
|
||||
modalEvent.danger("조회 오류", "조회 중 오류가 발생하였습니다. 잠시후 다시시도하십시오.");
|
||||
},
|
||||
beforeSend: function () {
|
||||
$(".loading-image-layer").show();
|
||||
},
|
||||
complete: function () {
|
||||
$(".loading-image-layer").hide();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function fn_nocheck(num){
|
||||
$("#checkboxID" + num).prop("checked",false);
|
||||
fn_deleteList(num);
|
||||
/****************************************************************************
|
||||
* Choices.js 초기화 및 옵션 업데이트
|
||||
****************************************************************************/
|
||||
function updateProcedureOptions(data) {
|
||||
priceList = data;
|
||||
|
||||
// 기존 Choices 인스턴스 제거
|
||||
if (procedureChoices) {
|
||||
procedureChoices.destroy();
|
||||
}
|
||||
|
||||
// 선택 옵션 데이터 생성
|
||||
const choices = data.map(item => {
|
||||
if (!item.MU_TREATMENT_PROCEDURE_ID || !item.TREATMENT_PROCEDURE_NAME) return null;
|
||||
|
||||
const basePrice = (item.PRICE || 0) + (item.VAT || 0);
|
||||
const discountPrice = item.DISCOUNT_PRICE;
|
||||
|
||||
return {
|
||||
value: item.MU_TREATMENT_PROCEDURE_ID,
|
||||
label: item.TREATMENT_PROCEDURE_NAME,
|
||||
customProperties: {
|
||||
name: item.TREATMENT_PROCEDURE_NAME,
|
||||
price: basePrice,
|
||||
discountPrice: discountPrice,
|
||||
originalData: item
|
||||
}
|
||||
};
|
||||
}).filter(Boolean);
|
||||
|
||||
// Choices.js 초기화
|
||||
procedureChoices = new Choices('#procedure-select', {
|
||||
removeItemButton: true,
|
||||
searchEnabled: true,
|
||||
searchPlaceholderValue: '시술명으로 검색...',
|
||||
placeholder: true,
|
||||
placeholderValue: '시술을 선택하세요',
|
||||
maxItemCount: -1,
|
||||
choices: choices,
|
||||
shouldSort: false,
|
||||
searchResultLimit: 10,
|
||||
searchFields: ['label', 'value'],
|
||||
itemSelectText: '',
|
||||
noChoicesText: '선택 가능한 시술이 없습니다',
|
||||
noResultsText: '검색 결과가 없습니다',
|
||||
loadingText: '시술 정보를 불러오는 중...',
|
||||
|
||||
// 템플릿 커스터마이징 - 선택된 항목에 가격 표시 추가
|
||||
callbackOnCreateTemplates: function (template) {
|
||||
return {
|
||||
// 선택된 항목 템플릿 - 가격 정보 포함
|
||||
item: ({ classNames }, data) => {
|
||||
const customProps = data.customProperties || {};
|
||||
const name = customProps.name || data.label;
|
||||
const price = customProps.price || 0;
|
||||
const discountPrice = customProps.discountPrice;
|
||||
|
||||
// 가격 표시 HTML 생성
|
||||
let priceHtml = '';
|
||||
if (discountPrice && discountPrice < price) {
|
||||
priceHtml = `
|
||||
<span class="selected-price-discount">${discountPrice.toLocaleString()}원</span>
|
||||
<span class="selected-price-original">${price.toLocaleString()}원</span>
|
||||
`;
|
||||
} else {
|
||||
priceHtml = `<span class="selected-price">${price.toLocaleString()}원</span>`;
|
||||
}
|
||||
|
||||
return template(`
|
||||
<div class="${classNames.item} ${data.highlighted ? classNames.highlightedState : classNames.itemSelectable}" data-item data-id="${data.id}" data-value="${data.value}">
|
||||
<span class="selected-item-content">
|
||||
<span class="selected-item-name">${name}</span>
|
||||
<span class="selected-item-price">${priceHtml}</span>
|
||||
</span>
|
||||
<button type="button" class="${classNames.button}" aria-label="Remove item: '${data.value}'" data-button>X</button>
|
||||
|
||||
</div>
|
||||
`);
|
||||
},
|
||||
|
||||
// 드롭다운 선택 옵션 템플릿
|
||||
choice: ({ classNames }, data) => {
|
||||
const customProps = data.customProperties || {};
|
||||
const name = customProps.name || data.label;
|
||||
const price = customProps.price || 0;
|
||||
const discountPrice = customProps.discountPrice;
|
||||
|
||||
let priceHtml = '';
|
||||
if (discountPrice && discountPrice < price) {
|
||||
priceHtml = `
|
||||
<span class="procedure-price-discount">${discountPrice.toLocaleString()}원</span>
|
||||
<span class="procedure-price-original">${price.toLocaleString()}원</span>
|
||||
`;
|
||||
} else {
|
||||
priceHtml = `<span class="procedure-price">${price.toLocaleString()}원</span>`;
|
||||
}
|
||||
|
||||
return template(`
|
||||
<div class="${classNames.item} ${classNames.itemChoice} ${data.disabled ? classNames.itemDisabled : classNames.itemSelectable}" data-select-text="" data-choice ${data.disabled ? 'data-choice-disabled aria-disabled="true"' : 'data-choice-selectable'} data-id="${data.id}" data-value="${data.value}">
|
||||
<div style="display: flex; justify-content: space-between; width: 100%; align-items: center;">
|
||||
<span>${name}</span>
|
||||
${priceHtml}
|
||||
</div>
|
||||
</div>
|
||||
`);
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
// 이벤트 리스너 추가
|
||||
const selectElement = document.getElementById('procedure-select');
|
||||
selectElement.addEventListener('change', function (event) {
|
||||
console.log('Selection changed:', event.detail);
|
||||
updateTotalPrice();
|
||||
});
|
||||
|
||||
selectElement.addEventListener('addItem', function (event) {
|
||||
console.log('Item added:', event.detail);
|
||||
updateTotalPrice();
|
||||
});
|
||||
|
||||
selectElement.addEventListener('removeItem', function (event) {
|
||||
console.log('Item removed:', event.detail);
|
||||
updateTotalPrice();
|
||||
});
|
||||
}
|
||||
|
||||
function fn_deleteList(num){
|
||||
|
||||
var price = $("#price_div1").text().replace(/,/g, "");
|
||||
var discount_price = $("#liid" + num + " > div.info > span > span").text().replace(/,/g, "");
|
||||
|
||||
price = Number(price) - Number(discount_price);
|
||||
$("#price_div1").text(price.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ","));
|
||||
$("#liid" + num).remove();
|
||||
}
|
||||
|
||||
function fn_moveReservation(category_div, category_no, post_no){
|
||||
let pageMoveForm = document.createElement('form');
|
||||
let obj = document.createElement('input');
|
||||
obj.setAttribute('type', 'hidden');
|
||||
obj.setAttribute('name', 'CATEGORY_DIV_CD');
|
||||
obj.setAttribute('value', "0"+category_div);
|
||||
pageMoveForm.appendChild(obj);
|
||||
|
||||
let obj2 = document.createElement('input');
|
||||
obj2.setAttribute('type', 'hidden');
|
||||
obj2.setAttribute('name', 'CATEGORY_NO');
|
||||
obj2.setAttribute('value', category_no);
|
||||
pageMoveForm.appendChild(obj2);
|
||||
|
||||
let obj3 = document.createElement('input');
|
||||
obj3.setAttribute('type', 'hidden');
|
||||
obj3.setAttribute('name', 'POST_NO');
|
||||
obj3.setAttribute('value', post_no);
|
||||
pageMoveForm.appendChild(obj3);
|
||||
|
||||
let obj4 = document.getElementsByName('procedure_id');
|
||||
var len = obj4.length;
|
||||
if(len == 0){
|
||||
alert('시술이 선택되지 않았습니다.');
|
||||
/****************************************************************************
|
||||
* 총 금액 업데이트 - 개선된 버전
|
||||
****************************************************************************/
|
||||
function updateTotalPrice() {
|
||||
if (!procedureChoices) {
|
||||
console.log('procedureChoices not initialized');
|
||||
return;
|
||||
}
|
||||
var value = '';
|
||||
for(var i = 0; i < len; i++){
|
||||
value += obj4[i].getAttribute('value') + '#';
|
||||
}
|
||||
|
||||
let obj5 = document.createElement('input');
|
||||
obj5.setAttribute('type', 'hidden');
|
||||
obj5.setAttribute('name', 'PROCEDURE_ID');
|
||||
obj5.setAttribute('value', value);
|
||||
pageMoveForm.appendChild(obj5);
|
||||
|
||||
pageMoveForm.setAttribute('method', 'post');
|
||||
pageMoveForm.setAttribute('action', '/webservice/selectMakeReservation.do');
|
||||
document.body.appendChild(pageMoveForm);
|
||||
|
||||
pageMoveForm.submit();
|
||||
|
||||
|
||||
const selectedValues = procedureChoices.getValue(true);
|
||||
console.log('Selected values:', selectedValues);
|
||||
|
||||
let total = 0;
|
||||
|
||||
selectedValues.forEach(value => {
|
||||
const item = priceList.find(p => p.MU_TREATMENT_PROCEDURE_ID == value);
|
||||
console.log('Found item for value', value, ':', item);
|
||||
|
||||
if (item) {
|
||||
const basePrice = (item.PRICE || 0) + (item.VAT || 0);
|
||||
const discountPrice = item.DISCOUNT_PRICE;
|
||||
|
||||
// 할인가가 있고 더 저렴하면 할인가 사용, 아니면 기본가격 사용
|
||||
const finalPrice = (discountPrice && discountPrice < basePrice) ? discountPrice : basePrice;
|
||||
total += finalPrice;
|
||||
console.log('Added price:', finalPrice);
|
||||
}
|
||||
});
|
||||
|
||||
console.log('Total calculated:', total);
|
||||
totalEl.textContent = total.toLocaleString() + '원';
|
||||
reserveBtn.disabled = selectedValues.length === 0;
|
||||
}
|
||||
|
||||
/* 버튼 클릭했는지 안했는지 확인 class active */
|
||||
var $selectProcedureDiv = $('.select_procedure_div');
|
||||
$selectProcedureDiv.click(function(e){
|
||||
e.stopPropagation();
|
||||
$selectProcedureDiv.not(this).removeClass('active'); /*remove buttonactive from the others*/
|
||||
$(this).toggleClass('active'); /*toggle current clicked element*/
|
||||
});
|
||||
/****************************************************************************
|
||||
* 예약 페이지로 이동
|
||||
****************************************************************************/
|
||||
function fn_moveReservation(category_div, category_no, post_no) {
|
||||
if (!procedureChoices) {
|
||||
alert('시술 선택 기능이 초기화되지 않았습니다.');
|
||||
return;
|
||||
}
|
||||
|
||||
$(window).on("click", function(){
|
||||
$selectProcedureDiv.removeClass('active');
|
||||
})
|
||||
const selectedValues = procedureChoices.getValue(true);
|
||||
|
||||
//초기화
|
||||
fn_init();
|
||||
if (selectedValues.length === 0) {
|
||||
alert('시술을 선택해주세요.');
|
||||
return;
|
||||
}
|
||||
|
||||
const form = document.createElement('form');
|
||||
form.method = 'post';
|
||||
form.action = '/webevent/selectMakeReservation.do';
|
||||
|
||||
// 기본 파라미터 추가
|
||||
const params = [
|
||||
{ name: 'CATEGORY_DIV_CD', value: category_div },
|
||||
{ name: 'CATEGORY_NO', value: category_no },
|
||||
{ name: 'POST_NO', value: post_no },
|
||||
{ name: 'EVENT_START_DT', value: window.eventStartDt || '' },
|
||||
{ name: 'EVENT_END_DT', value: window.eventEndDt || '' }
|
||||
];
|
||||
|
||||
params.forEach(param => {
|
||||
const input = document.createElement('input');
|
||||
input.type = 'hidden';
|
||||
input.name = param.name;
|
||||
input.value = param.value;
|
||||
form.appendChild(input);
|
||||
});
|
||||
|
||||
// 선택된 시술 추가
|
||||
selectedValues.forEach(value => {
|
||||
const input = document.createElement('input');
|
||||
input.type = 'hidden';
|
||||
input.name = 'PROCEDURE_ID';
|
||||
input.value = value;
|
||||
form.appendChild(input);
|
||||
});
|
||||
|
||||
document.body.appendChild(form);
|
||||
form.submit();
|
||||
}
|
||||
|
||||
// 예약 버튼 이벤트
|
||||
reserveBtn.addEventListener('click', function () {
|
||||
fn_moveReservation(category_div_cd, category_no, post_no);
|
||||
});
|
||||
@@ -1,189 +1,216 @@
|
||||
/************************************************
|
||||
* 초기화
|
||||
************************************************/
|
||||
function fn_init() {
|
||||
fn_SelectListCategory();
|
||||
}
|
||||
class EventManager {
|
||||
constructor() {
|
||||
this.events = [];
|
||||
this.categories = [];
|
||||
this.init();
|
||||
}
|
||||
|
||||
async init() {
|
||||
await this.loadCategories();
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* 이벤트 카테고리 목록 가져오기
|
||||
****************************************************************************/
|
||||
function fn_SelectListCategory(){
|
||||
async apiRequest(url, data) {
|
||||
return new Promise((resolve, reject) => {
|
||||
$.ajax({
|
||||
url: encodeURI(url),
|
||||
data: data,
|
||||
dataType: 'json',
|
||||
processData: false,
|
||||
contentType: false,
|
||||
type: 'POST',
|
||||
success: resolve,
|
||||
error: reject,
|
||||
beforeSend: () => $(".loading-image-layer").show(),
|
||||
complete: () => $(".loading-image-layer").hide()
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
let formData = new FormData();
|
||||
formData.append('bannerType', 'A');
|
||||
async loadCategories() {
|
||||
try {
|
||||
const formData = new FormData();
|
||||
formData.append('bannerType', 'A');
|
||||
const data = await this.apiRequest('/webevent/selectListWebEvent.do', formData);
|
||||
|
||||
$.ajax({
|
||||
url: encodeURI('/webevent/selectListWebEvent.do'),
|
||||
data: formData,
|
||||
dataType: 'json',
|
||||
processData: false,
|
||||
contentType: false,
|
||||
type: 'POST',
|
||||
async: true,
|
||||
success: function(data){
|
||||
if(data.msgCode=='0'){
|
||||
|
||||
let totalCount = data.rows.length;
|
||||
|
||||
if (0 < totalCount) {
|
||||
let listHTML = '';
|
||||
|
||||
for (let i = 0; i < data.rows.length; i++) {
|
||||
if(i == 0){
|
||||
listHTML += '<li class="active" id="category_'+data.rows[i].CATEGORY_NO+'">';
|
||||
listHTML += ' <a href="javascript:fn_SelectEventList(' + data.rows[i].CATEGORY_NO + ');">' + data.rows[i].CATEGORY_NM + '</a>';
|
||||
listHTML += '</li>';
|
||||
}else{
|
||||
listHTML += '<li class="nonactive" id="category_'+data.rows[i].CATEGORY_NO+'">';
|
||||
listHTML += ' <a href="javascript:fn_SelectEventList(' + data.rows[i].CATEGORY_NO + ');">' + data.rows[i].CATEGORY_NM + '</a>';
|
||||
listHTML += '</li>';
|
||||
}
|
||||
}
|
||||
$("#servicecategory").html(listHTML);
|
||||
fn_SelectEventList(data.rows[0].CATEGORY_NO);
|
||||
}
|
||||
}else{
|
||||
modalEvent.danger("조회 오류", data.msgDesc);
|
||||
}
|
||||
|
||||
},
|
||||
error : function(xhr, status, error) {
|
||||
modalEvent.danger("조회 오류", "조회 중 오류가 발생하였습니다. 잠시후 다시시도하십시오.");
|
||||
},
|
||||
beforeSend:function(){
|
||||
// 로딩열기
|
||||
$(".loading-image-layer").show();
|
||||
},
|
||||
complete:function(){
|
||||
// 로딩닫기
|
||||
$(".loading-image-layer").hide();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* 시술 목록 가져오기
|
||||
****************************************************************************/
|
||||
function fn_SelectEventList(category_no){
|
||||
$(".active").addClass("nonactive");
|
||||
$(".active").removeClass("active");
|
||||
|
||||
$("#category_"+category_no).removeClass("nonactive");
|
||||
$("#category_"+category_no).addClass("active");
|
||||
|
||||
|
||||
let formData = new FormData();
|
||||
formData.append('category_no', category_no);
|
||||
|
||||
$.ajax({
|
||||
url: encodeURI('/webevent/selectListEvent.do'),
|
||||
data: formData,
|
||||
dataType: 'json',
|
||||
processData: false,
|
||||
contentType: false,
|
||||
type: 'POST',
|
||||
async: true,
|
||||
success: function(data){
|
||||
if(data.msgCode=='0'){
|
||||
|
||||
let totalCount = data.rows.length;
|
||||
let listHTML = '';
|
||||
if (0 < totalCount) {
|
||||
for (let i = 0; i < data.rows.length; i++) {
|
||||
listHTML += '<li class="fadeIn">';
|
||||
listHTML += ' <a href="javascript:fn_moveDetail(' + data.rows[i].CATEGORY_DIV_CD +', ' + data.rows[i].CATEGORY_NO + ',' + data.rows[i].POST_NO + ');" class="event-card">';
|
||||
listHTML += ' <div class="img_box">';
|
||||
listHTML += ' <img src="https://intranet.bbgnetworks.com/uploadFiles/T00004/eventImg/20250203144112.png" alt="평일 오후1시-5시 / 피부 이벤트" class="">';
|
||||
listHTML += ' </div>';
|
||||
listHTML += ' <div class="txt-box">';
|
||||
listHTML += ' <!-- BEST/NEW 아이콘 퍼블리싱 -->';
|
||||
listHTML += ' <!-- __tit-best: best아이콘 __tit-new: new아이콘 -->';
|
||||
listHTML += ' <div class="tit-txt">';
|
||||
listHTML += ' <p class="multy-ellip2">' + data.rows[i].TITLE + '</p>';
|
||||
listHTML += ' </div>';
|
||||
listHTML += ' <span class="sub-txt max-line1 one-ellip">' + data.rows[i].THUMBNAIL_BOTTOM_TXT + '</span>';
|
||||
listHTML += ' <div class="ab_cont">';
|
||||
listHTML += ' <span class="cost"><del class="">';
|
||||
if(data.rows[i].DISCOUNT_PRICE == null || data.rows[i].DISCOUNT_PRICE == undefined){
|
||||
if(data.rows[i].PRICE == null || data.rows[i].PRICE == undefined){
|
||||
listHTML += '0';
|
||||
}else{
|
||||
listHTML += (data.rows[i].PRICE).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
|
||||
}
|
||||
}else{
|
||||
listHTML += (data.rows[i].DISCOUNT_PRICE).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
|
||||
}
|
||||
listHTML += ' </del>원</span>';
|
||||
listHTML += ' <span class="discount">';
|
||||
listHTML += ' <strong class="txt_num">';
|
||||
if(data.rows[i].DISCOUNT_PRICE == null || data.rows[i].DISCOUNT_PRICE == undefined){
|
||||
if(data.rows[i].PRICE == null || data.rows[i].PRICE == undefined){
|
||||
listHTML += '0';
|
||||
}else{
|
||||
listHTML += (data.rows[i].PRICE).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
|
||||
}
|
||||
}else{
|
||||
listHTML += (data.rows[i].DISCOUNT_PRICE).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
|
||||
}
|
||||
listHTML += ' </strong>원 부터 </span>';
|
||||
listHTML += ' </div>';
|
||||
listHTML += ' </div>';
|
||||
listHTML += ' </a>';
|
||||
listHTML += '</li>';
|
||||
}
|
||||
$("#detail_list").html(listHTML);
|
||||
|
||||
}else{
|
||||
listHTML += '<li class="mt70">';
|
||||
listHTML += '</li>';
|
||||
$("#detail_list").html(listHTML);
|
||||
if (data.msgCode === '0') {
|
||||
this.categories = data.rows;
|
||||
this.renderCategories();
|
||||
if (this.categories.length > 0) {
|
||||
this.loadEvents(this.categories[0].CATEGORY_NO);
|
||||
}
|
||||
}else{
|
||||
modalEvent.danger("조회 오류", data.msgDesc);
|
||||
}
|
||||
} else {
|
||||
modalEvent.danger("조회 오류", data.msgDesc);
|
||||
}
|
||||
} catch (error) {
|
||||
modalEvent.danger("조회 오류", "조회 중 오류가 발생하였습니다.");
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
error : function(xhr, status, error) {
|
||||
modalEvent.danger("조회 오류", "조회 중 오류가 발생하였습니다. 잠시후 다시시도하십시오.");
|
||||
},
|
||||
beforeSend:function(){
|
||||
// 로딩열기
|
||||
$(".loading-image-layer").show();
|
||||
},
|
||||
complete:function(){
|
||||
// 로딩닫기
|
||||
$(".loading-image-layer").hide();
|
||||
}
|
||||
});
|
||||
async loadEvents(categoryNo) {
|
||||
try {
|
||||
const formData = new FormData();
|
||||
formData.append('category_no', categoryNo);
|
||||
const data = await this.apiRequest('/webevent/selectListEvent.do', formData);
|
||||
|
||||
if (data.msgCode === '0') {
|
||||
const today = new Date();
|
||||
today.setHours(0, 0, 0, 0);
|
||||
|
||||
this.events = data.rows.map(row => {
|
||||
// 지난 이벤트 판별: EVENT_END_DT가 있고 오늘보다 이전이면 expired
|
||||
let isExpired = false;
|
||||
if (row.EVENT_END_DT) {
|
||||
const endDate = new Date(row.EVENT_END_DT);
|
||||
endDate.setHours(23, 59, 59, 999);
|
||||
isExpired = endDate < today;
|
||||
}
|
||||
|
||||
return {
|
||||
img: CDN_URL + row.THUMBNAIL_PATH,
|
||||
title: row.TITLE,
|
||||
desc: row.CONTENT,
|
||||
meta: row.THUMBNAIL_BOTTOM_TXT,
|
||||
price: {
|
||||
before: Number(row.PRICE) || 0,
|
||||
after: Number(row.DISCOUNT_PRICE) || 0
|
||||
},
|
||||
categoryDiv: row.CATEGORY_DIV_CD,
|
||||
categoryNo: row.CATEGORY_NO,
|
||||
postNo: row.POST_NO,
|
||||
eventStartDt: row.EVENT_START_DT,
|
||||
eventEndDt: row.EVENT_END_DT,
|
||||
isExpired: isExpired
|
||||
};
|
||||
});
|
||||
this.renderEvents();
|
||||
} else {
|
||||
modalEvent.danger("조회 오류", data.msgDesc);
|
||||
}
|
||||
} catch (error) {
|
||||
modalEvent.danger("조회 오류", "조회 중 오류가 발생하였습니다.");
|
||||
}
|
||||
}
|
||||
|
||||
renderCategories() {
|
||||
const html = this.categories.map((cat, idx) => `
|
||||
<li class="category-item">
|
||||
<a href="#" class="category-link ${idx === 0 ? 'active' : ''}"
|
||||
data-category="${cat.CATEGORY_NO}">${cat.CATEGORY_NM}</a>
|
||||
</li>
|
||||
`).join('');
|
||||
|
||||
document.getElementById('category-list').innerHTML = html;
|
||||
|
||||
document.querySelectorAll('.category-link').forEach(link => {
|
||||
link.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
document.querySelectorAll('.category-link').forEach(item =>
|
||||
item.classList.remove('active'));
|
||||
link.classList.add('active');
|
||||
this.loadEvents(link.dataset.category);
|
||||
document.querySelector('.event-list').scrollTop = 0;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
renderEvents() {
|
||||
const html = this.events.map(event => {
|
||||
const expiredClass = event.isExpired ? ' expired' : '';
|
||||
const expiredBadge = event.isExpired ? '<span class="expired-badge">종료된 이벤트</span>' : '';
|
||||
|
||||
// 이벤트 기간 표시
|
||||
let dateHtml = '';
|
||||
if (event.eventStartDt || event.eventEndDt) {
|
||||
const startStr = event.eventStartDt || '';
|
||||
const endStr = event.eventEndDt || '';
|
||||
if (startStr && endStr) {
|
||||
dateHtml = `<div class="event-date">📅 ${startStr} ~ ${endStr}</div>`;
|
||||
} else if (endStr) {
|
||||
dateHtml = `<div class="event-date">📅 ~ ${endStr}</div>`;
|
||||
}
|
||||
}
|
||||
|
||||
return `
|
||||
<div class="event-card${expiredClass}" data-category-div="${event.categoryDiv}"
|
||||
data-category-no="${event.categoryNo}" data-post-no="${event.postNo}"
|
||||
data-expired="${event.isExpired}">
|
||||
${expiredBadge}
|
||||
<div class="event-img">
|
||||
<img src="${event.img}" alt="${event.title}">
|
||||
</div>
|
||||
<div class="event-info">
|
||||
<div class="event-title">${event.title}</div>
|
||||
${event.meta ? `<div class="event-meta">${event.meta}</div>` : ''}
|
||||
${dateHtml}
|
||||
<div class="event-price">
|
||||
${event.price.before !== event.price.after
|
||||
? `<span style="text-decoration:line-through; color:#9ca3af; font-size:0.95em; margin-right:8px;">
|
||||
${event.price.before.toLocaleString()}원
|
||||
</span>`
|
||||
: ''}
|
||||
${event.price.after.toLocaleString()}원 부터
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`}).join('');
|
||||
|
||||
document.getElementById('event-grid').innerHTML = html;
|
||||
|
||||
// 카드 클릭 이벤트 추가
|
||||
document.querySelectorAll('.event-card').forEach(card => {
|
||||
card.addEventListener('click', () => {
|
||||
const isExpired = card.dataset.expired === 'true';
|
||||
|
||||
if (isExpired) {
|
||||
// 지난 이벤트 → 팝업 표시
|
||||
this.showExpiredPopup();
|
||||
return;
|
||||
}
|
||||
|
||||
const categoryDiv = card.dataset.categoryDiv;
|
||||
const categoryNo = card.dataset.categoryNo;
|
||||
const postNo = card.dataset.postNo;
|
||||
this.goToDetail(categoryDiv, categoryNo, postNo);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
showExpiredPopup() {
|
||||
const overlay = document.getElementById('expired-popup');
|
||||
if (overlay) {
|
||||
overlay.classList.add('active');
|
||||
}
|
||||
}
|
||||
|
||||
hideExpiredPopup() {
|
||||
const overlay = document.getElementById('expired-popup');
|
||||
if (overlay) {
|
||||
overlay.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
goToDetail(categoryDiv, categoryNo, postNo) {
|
||||
const form = document.createElement('form');
|
||||
form.method = 'get';
|
||||
form.action = '/webevent/selectEventDetailIntro.do';
|
||||
|
||||
const fields = [
|
||||
{ name: 'CATEGORY_DIV_CD', value: categoryDiv },
|
||||
{ name: 'CATEGORY_NO', value: categoryNo },
|
||||
{ name: 'POST_NO', value: postNo }
|
||||
];
|
||||
|
||||
fields.forEach(field => {
|
||||
const input = document.createElement('input');
|
||||
input.type = 'hidden';
|
||||
input.name = field.name;
|
||||
input.value = field.value;
|
||||
form.appendChild(input);
|
||||
});
|
||||
|
||||
document.body.appendChild(form);
|
||||
form.submit();
|
||||
}
|
||||
}
|
||||
|
||||
function fn_moveDetail(category_div, category_no, post_no){
|
||||
let pageMoveForm = document.createElement('form');
|
||||
let obj = document.createElement('input');
|
||||
obj.setAttribute('type', 'hidden');
|
||||
obj.setAttribute('name', 'CATEGORY_DIV_CD');
|
||||
obj.setAttribute('value', "0"+category_div);
|
||||
pageMoveForm.appendChild(obj);
|
||||
|
||||
let obj2 = document.createElement('input');
|
||||
obj2.setAttribute('type', 'hidden');
|
||||
obj2.setAttribute('name', 'CATEGORY_NO');
|
||||
obj2.setAttribute('value', category_no);
|
||||
pageMoveForm.appendChild(obj2);
|
||||
|
||||
let obj3 = document.createElement('input');
|
||||
obj3.setAttribute('type', 'hidden');
|
||||
obj3.setAttribute('name', 'POST_NO');
|
||||
obj3.setAttribute('value', post_no);
|
||||
pageMoveForm.appendChild(obj3);
|
||||
|
||||
pageMoveForm.setAttribute('method', 'post');
|
||||
pageMoveForm.setAttribute('action', '/webevent/selectEventDetailIntro.do');
|
||||
document.body.appendChild(pageMoveForm);
|
||||
pageMoveForm.submit();
|
||||
}
|
||||
|
||||
//초기화
|
||||
fn_init();
|
||||
const eventManager = new EventManager();
|
||||
@@ -0,0 +1,117 @@
|
||||
class ReviewDetailManager {
|
||||
constructor() {
|
||||
this.quill = null;
|
||||
this.init();
|
||||
}
|
||||
|
||||
async init() {
|
||||
if (!muProcedureReviewId) {
|
||||
this.showError('잘못된 접근입니다.');
|
||||
return;
|
||||
}
|
||||
await this.loadReview();
|
||||
}
|
||||
|
||||
async apiRequest(url, data) {
|
||||
return new Promise((resolve, reject) => {
|
||||
$.ajax({
|
||||
url: encodeURI(url),
|
||||
data: data,
|
||||
dataType: 'json',
|
||||
processData: false,
|
||||
contentType: false,
|
||||
type: 'POST',
|
||||
success: resolve,
|
||||
error: reject
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async loadReview() {
|
||||
try {
|
||||
const formData = new FormData();
|
||||
formData.append('muProcedureReviewId', muProcedureReviewId);
|
||||
|
||||
const data = await this.apiRequest('/webreview/selectProcedureReview.do', formData);
|
||||
|
||||
if (data.msgCode === '0' && data.rows) {
|
||||
this.renderReview(data.rows);
|
||||
} else {
|
||||
this.showError('게시글을 찾을 수 없습니다.');
|
||||
}
|
||||
} catch (error) {
|
||||
this.showError('게시글 조회 중 오류가 발생하였습니다.');
|
||||
}
|
||||
}
|
||||
|
||||
renderReview(review) {
|
||||
// 제목
|
||||
document.getElementById('review-title').textContent = review.title || '';
|
||||
document.getElementById('breadcrumb-title').textContent = review.title || '상세보기';
|
||||
|
||||
// 메타 정보
|
||||
document.getElementById('review-date').textContent = review.writeDate || '';
|
||||
document.getElementById('review-views').textContent = review.viewCount || 0;
|
||||
|
||||
// 해시태그
|
||||
const tagsContainer = document.getElementById('review-tags');
|
||||
if (review.hashtag) {
|
||||
const tags = review.hashtag.split(',').map(t => t.trim()).filter(t => t);
|
||||
tagsContainer.innerHTML = tags.map(tag =>
|
||||
`<span class="review-tag">#${tag}</span>`
|
||||
).join('');
|
||||
}
|
||||
|
||||
// Quill Delta JSON 본문 렌더링
|
||||
const contentDiv = document.getElementById('review-content');
|
||||
if (review.content) {
|
||||
try {
|
||||
// Base64 디코딩
|
||||
const decoded = decodeURIComponent(escape(atob(review.content)));
|
||||
const delta = JSON.parse(decoded);
|
||||
|
||||
// Quill read-only 모드로 렌더링
|
||||
contentDiv.innerHTML = ''; // 로딩 메시지 제거
|
||||
this.quill = new Quill(contentDiv, {
|
||||
readOnly: true,
|
||||
modules: { toolbar: false },
|
||||
theme: 'snow'
|
||||
});
|
||||
this.quill.setContents(delta);
|
||||
|
||||
// 툴바 숨기기 (snow 테마 기본 요소)
|
||||
const toolbar = contentDiv.parentElement.querySelector('.ql-toolbar');
|
||||
if (toolbar) toolbar.style.display = 'none';
|
||||
|
||||
// 에디터 테두리 제거
|
||||
const container = contentDiv.parentElement.querySelector('.ql-container');
|
||||
if (container) {
|
||||
container.style.border = 'none';
|
||||
container.style.fontSize = '16px';
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
// Delta 파싱 실패 시 HTML로 시도
|
||||
try {
|
||||
const html = decodeURIComponent(escape(atob(review.content)));
|
||||
contentDiv.innerHTML = html;
|
||||
} catch (e2) {
|
||||
contentDiv.innerHTML = review.content;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
contentDiv.innerHTML = '<p style="color: #9ca3af; text-align: center;">내용이 없습니다.</p>';
|
||||
}
|
||||
}
|
||||
|
||||
showError(msg) {
|
||||
document.getElementById('review-content').innerHTML = `
|
||||
<div style="text-align: center; padding: 3rem; color: #6b7280;">
|
||||
<div style="font-size: 2rem; margin-bottom: 1rem;">😔</div>
|
||||
<div>${msg}</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
const reviewDetailManager = new ReviewDetailManager();
|
||||
@@ -0,0 +1,259 @@
|
||||
class ReviewListManager {
|
||||
constructor() {
|
||||
this.reviews = [];
|
||||
this.currentPage = 1;
|
||||
this.pageSize = 9;
|
||||
this.totalCount = 0;
|
||||
this.categoryDivCd = '07'; // 다이어트센터
|
||||
this.sliders = {};
|
||||
this.init();
|
||||
}
|
||||
|
||||
async init() {
|
||||
this.bindEvents();
|
||||
await this.loadReviews();
|
||||
}
|
||||
|
||||
bindEvents() {
|
||||
document.getElementById('btnSearch').addEventListener('click', () => {
|
||||
this.currentPage = 1;
|
||||
this.loadReviews();
|
||||
});
|
||||
document.getElementById('searchTitle').addEventListener('keypress', (e) => {
|
||||
if (e.key === 'Enter') { this.currentPage = 1; this.loadReviews(); }
|
||||
});
|
||||
}
|
||||
|
||||
async apiRequest(url, data) {
|
||||
return new Promise((resolve, reject) => {
|
||||
$.ajax({
|
||||
url: encodeURI(url), data: data, dataType: 'json',
|
||||
processData: false, contentType: false, type: 'POST',
|
||||
success: resolve, error: reject
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async loadReviews() {
|
||||
try {
|
||||
const formData = new FormData();
|
||||
formData.append('categoryDivCd', this.categoryDivCd);
|
||||
formData.append('start', (this.currentPage - 1) * this.pageSize);
|
||||
formData.append('limit', this.pageSize);
|
||||
|
||||
const searchTitle = document.getElementById('searchTitle').value.trim();
|
||||
if (searchTitle) formData.append('title', searchTitle);
|
||||
|
||||
const data = await this.apiRequest('/webreview/selectListProcedureReview.do', formData);
|
||||
if (data.msgCode === '0') {
|
||||
this.reviews = data.rows || [];
|
||||
this.totalCount = parseInt(data.totalCount) || 0;
|
||||
this.renderReviews();
|
||||
this.renderPagination();
|
||||
} else {
|
||||
this.showEmpty('조회 중 오류가 발생하였습니다.');
|
||||
}
|
||||
} catch (error) {
|
||||
this.showEmpty('조회 중 오류가 발생하였습니다.');
|
||||
}
|
||||
}
|
||||
|
||||
/** Delta JSON에서 이미지 URL 배열 추출 */
|
||||
extractImages(content) {
|
||||
if (!content) return [];
|
||||
try {
|
||||
const decoded = decodeURIComponent(escape(atob(content)));
|
||||
const delta = JSON.parse(decoded);
|
||||
if (delta.ops) {
|
||||
return delta.ops
|
||||
.filter(op => op.insert && typeof op.insert === 'object' && op.insert.image)
|
||||
.map(op => op.insert.image);
|
||||
}
|
||||
return [];
|
||||
} catch (e) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/** Delta JSON에서 텍스트 요약 추출 */
|
||||
extractSummary(content) {
|
||||
if (!content) return '';
|
||||
try {
|
||||
const decoded = decodeURIComponent(escape(atob(content)));
|
||||
const delta = JSON.parse(decoded);
|
||||
if (delta.ops) {
|
||||
const text = delta.ops
|
||||
.filter(op => typeof op.insert === 'string')
|
||||
.map(op => op.insert).join('').replace(/\n/g, ' ').trim();
|
||||
return text.length > 100 ? text.substring(0, 100) + '...' : text;
|
||||
}
|
||||
return '';
|
||||
} catch (e) { return ''; }
|
||||
}
|
||||
|
||||
/** 이미지 슬라이더 HTML 생성 */
|
||||
buildSliderHtml(images, cardIdx) {
|
||||
if (images.length === 0) {
|
||||
return '<div class="review-no-image">📷</div>';
|
||||
}
|
||||
|
||||
const sliderId = `slider-${cardIdx}`;
|
||||
const imagesHtml = images.map(src =>
|
||||
`<img src="${src}" alt="고객후기" loading="lazy" />`
|
||||
).join('');
|
||||
|
||||
// 도트 인디케이터
|
||||
const dotsHtml = images.length > 1
|
||||
? `<div class="slider-dots">${images.map((_, i) =>
|
||||
`<button class="slider-dot ${i === 0 ? 'active' : ''}" data-idx="${i}"></button>`
|
||||
).join('')}</div>` : '';
|
||||
|
||||
// 화살표
|
||||
const arrowsHtml = images.length > 1
|
||||
? `<button class="slider-arrow prev" data-dir="-1">‹</button>
|
||||
<button class="slider-arrow next" data-dir="1">›</button>` : '';
|
||||
|
||||
// 이미지 카운트
|
||||
const countHtml = images.length > 1
|
||||
? `<span class="slider-count">1 / ${images.length}</span>` : '';
|
||||
|
||||
return `
|
||||
<div class="review-slider" data-slider-id="${sliderId}" data-total="${images.length}" data-current="0">
|
||||
<div class="review-slider-track">${imagesHtml}</div>
|
||||
${arrowsHtml}
|
||||
${dotsHtml}
|
||||
${countHtml}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
renderReviews() {
|
||||
const grid = document.getElementById('review-grid');
|
||||
|
||||
if (this.reviews.length === 0) {
|
||||
grid.innerHTML = `<div class="empty-state"><div class="icon">📝</div><div class="message">등록된 고객후기가 없습니다.</div></div>`;
|
||||
return;
|
||||
}
|
||||
|
||||
grid.innerHTML = this.reviews.map((review, idx) => {
|
||||
const images = this.extractImages(review.summary || review.content);
|
||||
const summaryText = this.extractSummary(review.summary || review.content);
|
||||
const sliderHtml = this.buildSliderHtml(images, idx);
|
||||
|
||||
let tagsHtml = '';
|
||||
if (review.hashtag) {
|
||||
const tags = review.hashtag.split(',').map(t => t.trim()).filter(t => t);
|
||||
tagsHtml = `<div class="review-card-tags">${tags.slice(0, 3).map(tag =>
|
||||
`<span class="review-tag">#${tag}</span>`).join('')}</div>`;
|
||||
}
|
||||
|
||||
return `
|
||||
<div class="review-card" data-id="${review.muProcedureReviewId}">
|
||||
${sliderHtml}
|
||||
<div class="review-card-body">
|
||||
<div class="review-card-title">${this.escapeHtml(review.title)}</div>
|
||||
<div class="review-card-summary">${summaryText}</div>
|
||||
${tagsHtml}
|
||||
<div class="review-card-footer">
|
||||
<span class="date">${review.writeDate}</span>
|
||||
<span class="views">👁 ${review.viewCount || 0}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}).join('');
|
||||
|
||||
// 슬라이더 이벤트 바인딩
|
||||
this.bindSliderEvents();
|
||||
|
||||
// 카드 클릭 이벤트 (슬라이더 요소 클릭 제외)
|
||||
grid.querySelectorAll('.review-card').forEach(card => {
|
||||
card.addEventListener('click', (e) => {
|
||||
// 슬라이더 화살표나 도트 클릭 시 이동 방지
|
||||
if (e.target.closest('.slider-arrow') || e.target.closest('.slider-dot')) return;
|
||||
const id = card.dataset.id;
|
||||
location.href = `/webreview/selectProcedureReviewIntro.do?muProcedureReviewId=${id}`;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/** 슬라이더 이벤트 바인딩 */
|
||||
bindSliderEvents() {
|
||||
document.querySelectorAll('.review-slider').forEach(slider => {
|
||||
const total = parseInt(slider.dataset.total);
|
||||
if (total <= 1) return;
|
||||
|
||||
const track = slider.querySelector('.review-slider-track');
|
||||
const dots = slider.querySelectorAll('.slider-dot');
|
||||
const countEl = slider.querySelector('.slider-count');
|
||||
|
||||
const goTo = (idx) => {
|
||||
const current = Math.max(0, Math.min(idx, total - 1));
|
||||
slider.dataset.current = current;
|
||||
track.style.transform = `translateX(-${current * 100}%)`;
|
||||
|
||||
dots.forEach((d, i) => d.classList.toggle('active', i === current));
|
||||
if (countEl) countEl.textContent = `${current + 1} / ${total}`;
|
||||
};
|
||||
|
||||
// 화살표 클릭
|
||||
slider.querySelectorAll('.slider-arrow').forEach(arrow => {
|
||||
arrow.addEventListener('click', (e) => {
|
||||
e.stopPropagation();
|
||||
const dir = parseInt(arrow.dataset.dir);
|
||||
let next = parseInt(slider.dataset.current) + dir;
|
||||
if (next < 0) next = total - 1;
|
||||
if (next >= total) next = 0;
|
||||
goTo(next);
|
||||
});
|
||||
});
|
||||
|
||||
// 도트 클릭
|
||||
dots.forEach(dot => {
|
||||
dot.addEventListener('click', (e) => {
|
||||
e.stopPropagation();
|
||||
goTo(parseInt(dot.dataset.idx));
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
renderPagination() {
|
||||
const area = document.getElementById('pagination-area');
|
||||
const totalPages = Math.ceil(this.totalCount / this.pageSize);
|
||||
if (totalPages <= 1) { area.innerHTML = ''; return; }
|
||||
|
||||
let html = `<button class="page-btn ${this.currentPage === 1 ? 'disabled' : ''}"
|
||||
data-page="${this.currentPage - 1}" ${this.currentPage === 1 ? 'disabled' : ''}>‹</button>`;
|
||||
const startPage = Math.max(1, this.currentPage - 2);
|
||||
const endPage = Math.min(totalPages, startPage + 4);
|
||||
for (let i = startPage; i <= endPage; i++) {
|
||||
html += `<button class="page-btn ${i === this.currentPage ? 'active' : ''}" data-page="${i}">${i}</button>`;
|
||||
}
|
||||
html += `<button class="page-btn ${this.currentPage === totalPages ? 'disabled' : ''}"
|
||||
data-page="${this.currentPage + 1}" ${this.currentPage === totalPages ? 'disabled' : ''}>›</button>`;
|
||||
area.innerHTML = html;
|
||||
|
||||
area.querySelectorAll('.page-btn:not(.disabled)').forEach(btn => {
|
||||
btn.addEventListener('click', () => {
|
||||
this.currentPage = parseInt(btn.dataset.page);
|
||||
this.loadReviews();
|
||||
window.scrollTo({ top: 0, behavior: 'smooth' });
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
showEmpty(msg) {
|
||||
document.getElementById('review-grid').innerHTML = `
|
||||
<div class="empty-state"><div class="icon">⚠️</div><div class="message">${msg}</div></div>`;
|
||||
document.getElementById('pagination-area').innerHTML = '';
|
||||
}
|
||||
|
||||
escapeHtml(str) {
|
||||
if (!str) return '';
|
||||
return str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>')
|
||||
.replace(/"/g, '"').replace(/'/g, ''');
|
||||
}
|
||||
}
|
||||
|
||||
const reviewListManager = new ReviewListManager();
|
||||
145
src/main/resources/static/sitemap.xml
Normal file
@@ -0,0 +1,145 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd">
|
||||
<url>
|
||||
<loc>https://diet.madeu.co.kr/</loc>
|
||||
<lastmod>2026-01-19T10:03:43+00:00</lastmod>
|
||||
<priority>1.00</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://diet.madeu.co.kr/index</loc>
|
||||
<lastmod>2026-01-19T10:03:43+00:00</lastmod>
|
||||
<priority>0.80</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://diet.madeu.co.kr/webintroduction/selectIntroductionHospitalIntro.do</loc>
|
||||
<lastmod>2026-01-19T10:03:43+00:00</lastmod>
|
||||
<priority>0.80</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://diet.madeu.co.kr/webintroduction/selectIntroductionStaffIntro.do</loc>
|
||||
<lastmod>2026-01-19T10:03:43+00:00</lastmod>
|
||||
<priority>0.80</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://diet.madeu.co.kr/webintroduction/selectIntroductionWayIntro.do</loc>
|
||||
<lastmod>2026-01-19T10:03:43+00:00</lastmod>
|
||||
<priority>0.80</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://diet.madeu.co.kr/webservice/selectServiceIntro.do?categoryNo=1</loc>
|
||||
<lastmod>2026-01-19T10:03:43+00:00</lastmod>
|
||||
<priority>0.80</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://diet.madeu.co.kr/webservice/selectServiceDetailIntro.do?categoryDivCd=01&categoryNo=1&postNo=1</loc>
|
||||
<lastmod>2026-01-19T10:03:43+00:00</lastmod>
|
||||
<priority>0.80</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://diet.madeu.co.kr/webservice/selectServiceIntro.do?categoryNo=8</loc>
|
||||
<lastmod>2026-01-19T10:03:43+00:00</lastmod>
|
||||
<priority>0.80</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://diet.madeu.co.kr/webservice/selectServiceDetailIntro.do?categoryDivCd=01&categoryNo=8&postNo=3</loc>
|
||||
<lastmod>2026-01-19T10:03:43+00:00</lastmod>
|
||||
<priority>0.80</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://diet.madeu.co.kr/webservice/selectServiceDetailIntro.do?categoryDivCd=01&categoryNo=8&postNo=2</loc>
|
||||
<lastmod>2026-01-19T10:03:43+00:00</lastmod>
|
||||
<priority>0.80</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://diet.madeu.co.kr/webservice/selectServiceIntro.do?categoryNo=2</loc>
|
||||
<lastmod>2026-01-19T10:03:43+00:00</lastmod>
|
||||
<priority>0.80</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://diet.madeu.co.kr/webservice/selectServiceDetailIntro.do?categoryDivCd=01&categoryNo=2&postNo=1</loc>
|
||||
<lastmod>2026-01-19T10:03:43+00:00</lastmod>
|
||||
<priority>0.80</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://diet.madeu.co.kr/webservice/selectServiceDetailIntro.do?categoryDivCd=01&categoryNo=2&postNo=2</loc>
|
||||
<lastmod>2026-01-19T10:03:43+00:00</lastmod>
|
||||
<priority>0.80</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://diet.madeu.co.kr/webservice/selectServiceIntro.do?categoryNo=6</loc>
|
||||
<lastmod>2026-01-19T10:03:43+00:00</lastmod>
|
||||
<priority>0.80</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://diet.madeu.co.kr/webservice/selectServiceDetailIntro.do?categoryDivCd=01&categoryNo=6&postNo=1</loc>
|
||||
<lastmod>2026-01-19T10:03:43+00:00</lastmod>
|
||||
<priority>0.80</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://diet.madeu.co.kr/webservice/selectServiceDetailIntro.do?categoryDivCd=01&categoryNo=6&postNo=2</loc>
|
||||
<lastmod>2026-01-19T10:03:43+00:00</lastmod>
|
||||
<priority>0.80</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://diet.madeu.co.kr/webservice/selectServiceIntro.do?categoryNo=3</loc>
|
||||
<lastmod>2026-01-19T10:03:43+00:00</lastmod>
|
||||
<priority>0.80</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://diet.madeu.co.kr/webservice/selectServiceDetailIntro.do?categoryDivCd=01&categoryNo=3&postNo=2</loc>
|
||||
<lastmod>2026-01-19T10:03:43+00:00</lastmod>
|
||||
<priority>0.80</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://diet.madeu.co.kr/webservice/selectServiceDetailIntro.do?categoryDivCd=01&categoryNo=3&postNo=1</loc>
|
||||
<lastmod>2026-01-19T10:03:43+00:00</lastmod>
|
||||
<priority>0.80</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://diet.madeu.co.kr/webservice/selectServiceDetailIntro.do?categoryDivCd=01&categoryNo=3&postNo=6</loc>
|
||||
<lastmod>2026-01-19T10:03:43+00:00</lastmod>
|
||||
<priority>0.80</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://diet.madeu.co.kr/webservice/selectServiceDetailIntro.do?categoryDivCd=01&categoryNo=3&postNo=7</loc>
|
||||
<lastmod>2026-01-19T10:03:43+00:00</lastmod>
|
||||
<priority>0.80</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://diet.madeu.co.kr/webservice/selectServiceDetailIntro.do?categoryDivCd=01&categoryNo=3&postNo=8</loc>
|
||||
<lastmod>2026-01-19T10:03:43+00:00</lastmod>
|
||||
<priority>0.80</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://diet.madeu.co.kr/webservice/selectServiceIntro.do?categoryNo=5</loc>
|
||||
<lastmod>2026-01-19T10:03:43+00:00</lastmod>
|
||||
<priority>0.80</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://diet.madeu.co.kr/webservice/selectServiceDetailIntro.do?categoryDivCd=01&categoryNo=5&postNo=1</loc>
|
||||
<lastmod>2026-01-19T10:03:43+00:00</lastmod>
|
||||
<priority>0.80</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://diet.madeu.co.kr/webevent/selectListWebEventIntro.do</loc>
|
||||
<lastmod>2026-01-19T10:03:43+00:00</lastmod>
|
||||
<priority>0.80</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://diet.madeu.co.kr/webphoto/selectListWebPhotoIntro.do</loc>
|
||||
<lastmod>2026-01-19T10:03:43+00:00</lastmod>
|
||||
<priority>0.80</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://diet.madeu.co.kr/webaccept/acceptSite.do</loc>
|
||||
<lastmod>2026-01-19T10:03:43+00:00</lastmod>
|
||||
<priority>0.80</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://diet.madeu.co.kr/webaccept/acceptPrivacy.do</loc>
|
||||
<lastmod>2026-01-19T10:03:43+00:00</lastmod>
|
||||
<priority>0.80</priority>
|
||||
</url>
|
||||
</urlset>
|
||||
@@ -1,125 +1,135 @@
|
||||
<!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"
|
||||
layout:decorate="~{/web/layout/layout}">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"
|
||||
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{/web/layout/layout}">
|
||||
<th:block layout:fragment="layoutCss">
|
||||
<link rel="stylesheet" href="/css/web/index.css">
|
||||
<link rel="stylesheet" href="/css/web/instagram-swiper-custom.css?v1.0">
|
||||
<link rel="stylesheet" href="/css/web/index.css">
|
||||
<link rel="stylesheet" href="/css/web/instagram-swiper-custom.css?v1.0">
|
||||
</th:block>
|
||||
<th:block layout:fragment="layoutContent">
|
||||
<div class="popup-background-mask"></div>
|
||||
<main>
|
||||
<section class="banner main_banner">
|
||||
<div class="swiper main_banner_swiper">
|
||||
<div class="swiper-wrapper" id="mainBannerList">
|
||||
</div>
|
||||
<div class="swiper-pagination main_banner_pagination"></div>
|
||||
</div>
|
||||
</section>
|
||||
<section class="cont content1">
|
||||
<div class="swiper-area">
|
||||
<h3>
|
||||
<span style="color:#fff;">MADE U</span> <span style="color:#ffcccc">시그니처</span>
|
||||
</h3>
|
||||
<div class="swiper cont1_swiper">
|
||||
<div class="swiper-wrapper">
|
||||
<div class="swiper-slide">
|
||||
<img class="pc" src="/image/main-slide/signature_diet_medicine_20251001.jpg" alt="img">
|
||||
<img class="mb" src="/image/main-slide/signature_diet_medicine_20251001.jpg" alt="img">
|
||||
</div>
|
||||
<div class="swiper-slide">
|
||||
<img class="pc" src="/image/main-slide/지방분해주사_20250911.jpg" alt="img">
|
||||
<img class="mb" src="/image/main-slide/지방분해주사_20250911.jpg" alt="img">
|
||||
</div>
|
||||
</div>
|
||||
<div class="swiper-pagination cont1_swiper_pagination"></div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section class="banner sub_banner">
|
||||
<div class="swiper sub_banner_swiper">
|
||||
<div class="swiper-wrapper" id="subBannerList">
|
||||
<div class="swiper-slide" style="background:#666;">
|
||||
<button class="detail_btn">Detail view ></button>
|
||||
</div>
|
||||
<div class="swiper-slide" style="background:#999;">
|
||||
<button class="detail_btn">Detail view ></button>
|
||||
</div>
|
||||
<div class="swiper-slide" style="background:#ddd;">
|
||||
<button class="detail_btn">Detail view ></button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="swiper-pagination sub_banner_pagination"></div>
|
||||
</div>
|
||||
</section>
|
||||
<section class="cont content2">
|
||||
<div class="swiper-area">
|
||||
<h3>
|
||||
<span style="color:#cc3333">MADE U</span> <span style="color:#000">프리미엄장비</span>
|
||||
</h3>
|
||||
<div class="swiper cont2_swiper">
|
||||
<div class="swiper-wrapper">
|
||||
<div class="swiper-slide">
|
||||
<img class="pc" src="/image/main-slide/울핏_20250911.jpg" alt="img">
|
||||
<img class="mb" src="/image/main-slide/울핏_20250911.jpg" alt="img">
|
||||
</div>
|
||||
<div class="swiper-slide">
|
||||
<img class="pc" src="/image/main-slide/튠바디_20250911.jpg" alt="img">
|
||||
<img class="mb" src="/image/main-slide/튠바디_20250911.jpg" alt="img">
|
||||
</div>
|
||||
</div>
|
||||
<div class="swiper-pagination cont2_swiper_pagination"></div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section class="instagram">
|
||||
<div class="swiper-area">
|
||||
<div class="instagram_top">
|
||||
<h3>
|
||||
MADE U 인스타그램<br/>
|
||||
<span>@madeu_diet</span>
|
||||
</h3>
|
||||
<button class="more_btn" onClick="window.open('https://instagram.com/madeu_diet')">View more ≫</button>
|
||||
<div class="popup-background-mask"></div>
|
||||
<main>
|
||||
<section class="banner main_banner">
|
||||
<div class="swiper main_banner_swiper">
|
||||
<div class="swiper-wrapper" id="mainBannerList">
|
||||
</div>
|
||||
<div class="swiper-pagination main_banner_pagination"></div>
|
||||
</div>
|
||||
<div class="instagram_btm" id="instagramFeed">
|
||||
<div class="swiper instagram_swiper">
|
||||
<div class="swiper-wrapper" id="instagramList">
|
||||
</div>
|
||||
</div>
|
||||
<!-- 커스텀 내비게이션 하단 영역 -->
|
||||
<div class="instagram-swiper-footer">
|
||||
<button class="custom-swiper-btn prev"></button>
|
||||
<div class="custom-swiper-thumbs"></div>
|
||||
<button class="custom-swiper-btn next"></button>
|
||||
</div>
|
||||
</section>
|
||||
<section class="cont content1">
|
||||
<div class="swiper-area">
|
||||
<h3>
|
||||
<span style="color:#fff;">MADE U</span> <span style="color:#ffcccc">시그니처</span>
|
||||
</h3>
|
||||
<div class="swiper cont1_swiper">
|
||||
<div class="swiper-wrapper">
|
||||
<div class="swiper-slide">
|
||||
<img class="pc" src="/image/main-slide/signature_diet_medicine_20251001.jpg" alt="img">
|
||||
<img class="mb" src="/image/main-slide/signature_diet_medicine_20251001.jpg" alt="img">
|
||||
</div>
|
||||
<div class="swiper-slide">
|
||||
<img class="pc" src="/image/signature/20251024/쉬다주사.jpg" alt="img">
|
||||
<img class="mb" src="/image/signature/20251024/쉬다주사.jpg" alt="img">
|
||||
</div>
|
||||
</div>
|
||||
<div class="swiper-pagination cont1_swiper_pagination"></div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section class="banner sub_banner">
|
||||
<div class="swiper sub_banner_swiper">
|
||||
<div class="swiper-wrapper" id="subBannerList">
|
||||
<div class="swiper-slide" style="background:#666;">
|
||||
<button class="detail_btn">Detail view ></button>
|
||||
</div>
|
||||
<div class="swiper-slide" style="background:#999;">
|
||||
<button class="detail_btn">Detail view ></button>
|
||||
</div>
|
||||
<div class="swiper-slide" style="background:#ddd;">
|
||||
<button class="detail_btn">Detail view ></button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="swiper-pagination sub_banner_pagination"></div>
|
||||
</div>
|
||||
</section>
|
||||
<section class="cont content2">
|
||||
<div class="swiper-area">
|
||||
<h3>
|
||||
<span style="color:#cc3333">MADE U</span> <span style="color:#000">프리미엄장비</span>
|
||||
</h3>
|
||||
<div class="swiper cont2_swiper">
|
||||
<div class="swiper-wrapper">
|
||||
<div class="swiper-slide">
|
||||
<img class="pc" src="/image/equip/20260313/온다리프팅(Body).jpg" alt="img">
|
||||
<img class="mb" src="/image/equip/20260313/온다리프팅(Body).jpg" alt="img">
|
||||
</div>
|
||||
<div class="swiper-slide">
|
||||
<img class="pc" src="/image/equip/20260313/튠바디.jpg" alt="img">
|
||||
<img class="mb" src="/image/equip/20260313/튠바디.jpg" alt="img">
|
||||
</div>
|
||||
|
||||
|
||||
<div class="swiper-slide">
|
||||
<img class="pc" src="/image/equip/20260313/티타늄리프팅(Body).jpg" alt="img">
|
||||
<img class="mb" src="/image/equip/20260313/티타늄리프팅(Body).jpg" alt="img">
|
||||
</div>
|
||||
<div class="swiper-slide">
|
||||
<img class="pc" src="/image/equip/20260313/울핏.jpg" alt="img">
|
||||
<img class="mb" src="/image/equip/20260313/울핏.jpg" alt="img">
|
||||
</div>
|
||||
</div>
|
||||
<div class="swiper-pagination cont2_swiper_pagination"></div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section class="instagram">
|
||||
<div class="swiper-area">
|
||||
<div class="instagram_top">
|
||||
<h3>
|
||||
MADE U 인스타그램<br />
|
||||
<span>@madeu_diet</span>
|
||||
</h3>
|
||||
<button class="more_btn" onClick="window.open('https://instagram.com/madeu_diet')">View more
|
||||
≫</button>
|
||||
</div>
|
||||
<div class="instagram_btm" id="instagramFeed">
|
||||
<div class="swiper instagram_swiper">
|
||||
<div class="swiper-wrapper" id="instagramList">
|
||||
</div>
|
||||
</div>
|
||||
<!-- 커스텀 내비게이션 하단 영역 -->
|
||||
<div class="instagram-swiper-footer">
|
||||
<button class="custom-swiper-btn prev"></button>
|
||||
<div class="custom-swiper-thumbs"></div>
|
||||
<button class="custom-swiper-btn next"></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<!-- 팝업 -->
|
||||
<div class="popup">
|
||||
<div class="top">
|
||||
<div class="tab-content" id="popupContentList"></div>
|
||||
<ul class="nav nav-tabs" role="tablist" id="popupTabList"></ul>
|
||||
</div>
|
||||
<div class="btm">
|
||||
<div class="left_box">
|
||||
<input type="checkbox" id="today" />
|
||||
<label for="today">오늘 하루 안보기</label>
|
||||
</div>
|
||||
<div class="right_box">
|
||||
<button class="close_btn_btm" id="btnPopupClose"><img src="/image/web/close.png" alt="close" /></button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<!-- 팝업 -->
|
||||
<div class="popup">
|
||||
<div class="top">
|
||||
<div class="tab-content" id="popupContentList"></div>
|
||||
<ul class="nav nav-tabs" role="tablist" id="popupTabList"></ul>
|
||||
</div>
|
||||
<div class="btm">
|
||||
<div class="left_box">
|
||||
<input type="checkbox" id="today"/>
|
||||
<label for="today">오늘 하루 안보기</label>
|
||||
</div>
|
||||
<div class="right_box">
|
||||
<button class="close_btn_btm" id="btnPopupClose"><img src="/image/web/close.png" alt="close"/></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</th:block>
|
||||
<th:block layout:fragment="layoutContentScript">
|
||||
<script>
|
||||
// CDN_URL 전역 정의
|
||||
const CDN_URL = "[(${@environment.getProperty('url.cdn')})]";
|
||||
</script>
|
||||
<script src="/js/web/index.js"></script>
|
||||
</script>
|
||||
<script src="/js/web/index.js"></script>
|
||||
</th:block>
|
||||
|
||||
</html>
|
||||
@@ -1,8 +1,6 @@
|
||||
<!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"
|
||||
layout:decorate="~{/web/layout/layout}">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"
|
||||
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{/web/layout/layout}">
|
||||
<th:block layout:fragment="layoutCss">
|
||||
<link rel="stylesheet" href="/css/web/introduction/introductionHospitalSelect.css?v1.0">
|
||||
</th:block>
|
||||
@@ -10,16 +8,16 @@
|
||||
<main>
|
||||
<section class="main_img">
|
||||
<div class="inner_wrap">
|
||||
<img src="/image/web/introduction_main.png" alt="introduction"/>
|
||||
<img src="/image/web/introduction_main.png" alt="introduction" />
|
||||
<div class="text_box">
|
||||
<p class="hashtag">#맞춤진료 #만족스러운결과</p>
|
||||
<p class="title">
|
||||
<span>MADE U 강남본점</span><br/>
|
||||
Total Beauty<br class="mb"/> One Stop System
|
||||
<span>MADE U 강남본점</span><br />
|
||||
Total Beauty<br class="mb" /> One Stop System
|
||||
</p>
|
||||
<p class="sub_text">
|
||||
한 공간에서 고객 한분 한분께 자연스러운<br class="mb"/> 아름다움과 건강한 다이어트를 위해<br/>
|
||||
항상 노력하는 함께하는<br class="mb"/> 조언자가 되어 드릴 것을 약속드립니다.
|
||||
한 공간에서 고객 한분 한분께 자연스러운<br class="mb" /> 아름다움과 건강한 다이어트를 위해<br />
|
||||
항상 노력하는 함께하는<br class="mb" /> 조언자가 되어 드릴 것을 약속드립니다.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -28,47 +26,47 @@
|
||||
<div class="inner_wrap">
|
||||
<div class="text_box">
|
||||
<p class="hashtag">#차별화된 맞춤 플랜</p>
|
||||
<p class="title">Looking Around<br/>MADE <span class="red">U</span></p>
|
||||
<p class="title">Looking Around<br />MADE <span class="red">U</span></p>
|
||||
<p class="sub_title">
|
||||
쾌적하고 안락한 공간을 제공하는<br/>
|
||||
쾌적하고 안락한 공간을 제공하는<br />
|
||||
<span>메이드유 강남본점</span>
|
||||
</p>
|
||||
<p class="sub_text">
|
||||
깨끗한 공간, 친절한 상담을 제공<span class="pc">하겠습니다.</span>
|
||||
</p>
|
||||
</div>
|
||||
<img src="/image/web/introduction_content1.png" alt="content1"/>
|
||||
<img src="/image/web/introduction_content1.png" alt="content1" />
|
||||
</div>
|
||||
</section>
|
||||
<section class="content2">
|
||||
<div class="inner_wrap">
|
||||
<p class="mb">쉬운다이어트 방법 없을까?<br/><span>MADE U 시그니처 프로그램</span></p>
|
||||
<p class="mb">쉬운다이어트 방법 없을까?<br /><span>MADE U 시그니처 프로그램</span></p>
|
||||
<ul>
|
||||
<li>
|
||||
<img src="/image/web/introduction_content2-1.png" alt="content2-1"/>
|
||||
<img src="/image/web/introduction_content2-1.png" alt="content2-1" />
|
||||
<div class="text_box">
|
||||
<p>One-On-One Customized Counseling</p>
|
||||
<p class="sub_title">
|
||||
MADE U 강남본점만의<br/>
|
||||
MADE U 강남본점만의<br />
|
||||
<span>1:1 맞춤 상담</span>
|
||||
</p>
|
||||
<p class="sub_text">
|
||||
고객님의 체형별 그리고 고민<br class="mb"/> 부위별 맞춤진료로,<br/>
|
||||
고객님의 체형별 그리고 고민<br class="mb" /> 부위별 맞춤진료로,<br />
|
||||
숨겨진 아름다움을 찾아드립니다.
|
||||
</p>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<img src="/image/web/introduction_content2-2.png" alt="content2-2"/>
|
||||
<img src="/image/web/introduction_content2-2.png" alt="content2-2" />
|
||||
<div class="text_box">
|
||||
<p>Trademark Application</p>
|
||||
<p class="sub_title">
|
||||
MADE U 강남본점만의<br/>
|
||||
MADE U 강남본점만의<br />
|
||||
<span>특허<span class="red">✓</span>상표 출원</span>
|
||||
</p>
|
||||
<p class="sub_text">
|
||||
메이드유 강남본점에서 자체 개발한<br class="mb"/> 메쉬다 주사<br class="pc"/>
|
||||
레시피의 뛰어난<br class="mb"/> 효과를 바탕으로 다수의<br/>
|
||||
메이드유 강남본점에서 자체 개발한<br class="mb" /> 메쉬다 주사<br class="pc" />
|
||||
레시피의 뛰어난<br class="mb" /> 효과를 바탕으로 다수의<br />
|
||||
특허와 상표를 등록했습니다.
|
||||
</p>
|
||||
</div>
|
||||
@@ -77,151 +75,151 @@
|
||||
</div>
|
||||
</section>
|
||||
<section class="content3">
|
||||
<div class="inner_wrap">
|
||||
<p>국내 고가명품 최다보유<br><span>MADE U</span> 프리미엄 장비 소개</p>
|
||||
|
||||
<div class="equipment-grid">
|
||||
<div class="equipment-card">
|
||||
<div class="equipment-image">
|
||||
<img src="/image/equip/써마지.jpg" alt="써마지">
|
||||
</div>
|
||||
<div class="equipment-info">
|
||||
<h3 class="equipment-name">써마지</h3>
|
||||
<p class="equipment-desc">깊은 탄력과 리프팅을 동시에</p>
|
||||
<p class="equipment-price"><!-- 1,800,000<span>부터</span> --></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="equipment-card">
|
||||
<div class="equipment-image">
|
||||
<img src="/image/equip/울쎄라.jpg" alt="울쎄라">
|
||||
</div>
|
||||
<div class="equipment-info">
|
||||
<h3 class="equipment-name">울쎄라</h3>
|
||||
<p class="equipment-desc">깊은 탄력과 리프팅을 동시에</p>
|
||||
<p class="equipment-price"><!-- 400,000<span>부터</span> --></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="equipment-card">
|
||||
<div class="equipment-image">
|
||||
<img src="/image/equip/티타늄.jpg" alt="티타늄리프팅">
|
||||
</div>
|
||||
<div class="equipment-info">
|
||||
<h3 class="equipment-name">티타늄리프팅</h3>
|
||||
<p class="equipment-desc">깊은 탄력과 리프팅을 동시에</p>
|
||||
<p class="equipment-price"><!-- 600,000<span>부터</span> --></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="equipment-card">
|
||||
<div class="equipment-image">
|
||||
<img src="/image/equip/튠바디.jpg" alt="튠바디">
|
||||
</div>
|
||||
<div class="equipment-info">
|
||||
<h3 class="equipment-name">튠바디</h3>
|
||||
<p class="equipment-desc">깊은 탄력과 리프팅을 동시에</p>
|
||||
<p class="equipment-price"><!-- 150,000<span>부터</span> --></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 2번째 행 -->
|
||||
<div class="equipment-card">
|
||||
<div class="equipment-image">
|
||||
<img src="/image/equip/튠페이스.jpg" alt="튠페이스">
|
||||
</div>
|
||||
<div class="equipment-info">
|
||||
<h3 class="equipment-name">튠페이스</h3>
|
||||
<p class="equipment-desc">깊은 탄력과 리프팅을 동시에</p>
|
||||
<p class="equipment-price"><!-- 400,000<span>부터</span> --></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="equipment-card">
|
||||
<div class="equipment-image">
|
||||
<img src="/image/equip/울핏.jpg" alt="울핏">
|
||||
</div>
|
||||
<div class="equipment-info">
|
||||
<h3 class="equipment-name">울핏</h3>
|
||||
<p class="equipment-desc">깊은 탄력과 리프팅을 동시에</p>
|
||||
<p class="equipment-price"><!-- 50,000<span>부터</span> --></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="equipment-card">
|
||||
<div class="equipment-image">
|
||||
<img src="/image/equip/포텐자.jpg" alt="포텐자">
|
||||
</div>
|
||||
<div class="equipment-info">
|
||||
<h3 class="equipment-name">포텐자</h3>
|
||||
<p class="equipment-desc">깊은 탄력과 리프팅을 동시에</p>
|
||||
<p class="equipment-price"><!-- 150,000<span>부터</span> --></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="equipment-card">
|
||||
<div class="equipment-image">
|
||||
<img src="/image/equip/인모드.jpg" alt="인모드">
|
||||
</div>
|
||||
<div class="equipment-info">
|
||||
<h3 class="equipment-name">인모드</h3>
|
||||
<p class="equipment-desc">깊은 탄력과 리프팅을 동시에</p>
|
||||
<p class="equipment-price"><!-- 100,000<span>부터</span> --></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 3번째 행 -->
|
||||
<div class="equipment-card">
|
||||
<div class="equipment-image">
|
||||
<img src="/image/equip/슈링크유니버스.jpg" alt="슈링크유니버스">
|
||||
</div>
|
||||
<div class="equipment-info">
|
||||
<h3 class="equipment-name">슈링크유니버스</h3>
|
||||
<p class="equipment-desc">깊은 탄력과 리프팅을 동시에</p>
|
||||
<p class="equipment-price"><!-- 89,000<span>부터</span> --></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="equipment-card">
|
||||
<div class="equipment-image">
|
||||
<img src="/image/equip/바디고주파테라피.jpg" alt="바디고주파테라피">
|
||||
</div>
|
||||
<div class="equipment-info">
|
||||
<h3 class="equipment-name">바디고주파테라피</h3>
|
||||
<p class="equipment-desc">깊은 탄력과 리프팅을 동시에</p>
|
||||
<p class="equipment-price"><!-- 100,000<span>부터</span> --></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="equipment-card">
|
||||
<div class="equipment-image">
|
||||
<img src="/image/equip/리포덤.jpg" alt="리포덤">
|
||||
</div>
|
||||
<div class="equipment-info">
|
||||
<h3 class="equipment-name">리포덤</h3>
|
||||
<p class="equipment-desc">깊은 탄력과 리프팅을 동시에</p>
|
||||
<p class="equipment-price"><!-- 50,000<span>부터</span> --></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="equipment-card">
|
||||
<div class="equipment-image">
|
||||
<img src="/image/equip/라비앙.jpg" alt="라비앙">
|
||||
</div>
|
||||
<div class="equipment-info">
|
||||
<h3 class="equipment-name">라비앙</h3>
|
||||
<p class="equipment-desc">깊은 탄력과 리프팅을 동시에</p>
|
||||
<p class="equipment-price"><!-- 165,000<span>부터</span> --></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="inner_wrap">
|
||||
<p>국내 고가명품 최다보유<br><span>MADE U</span> 프리미엄 장비 소개</p>
|
||||
|
||||
<div class="equipment-grid">
|
||||
<div class="equipment-card">
|
||||
<div class="equipment-image">
|
||||
<img src="/image/equip/써마지.jpg" alt="써마지">
|
||||
</div>
|
||||
<div class="equipment-info">
|
||||
<h3 class="equipment-name">써마지</h3>
|
||||
<!-- <p class="equipment-desc">깊은 탄력과 리프팅을 동시에</p> -->
|
||||
<p class="equipment-price"><!-- 1,800,000<span>부터</span> --></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="equipment-card">
|
||||
<div class="equipment-image">
|
||||
<img src="/image/equip/울쎄라.jpg" alt="울쎄라">
|
||||
</div>
|
||||
<div class="equipment-info">
|
||||
<h3 class="equipment-name">울쎄라</h3>
|
||||
<!-- <p class="equipment-desc">깊은 탄력과 리프팅을 동시에</p> -->
|
||||
<p class="equipment-price"><!-- 400,000<span>부터</span> --></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="equipment-card">
|
||||
<div class="equipment-image">
|
||||
<img src="/image/equip/티타늄.jpg" alt="티타늄리프팅">
|
||||
</div>
|
||||
<div class="equipment-info">
|
||||
<h3 class="equipment-name">티타늄리프팅</h3>
|
||||
<!-- <p class="equipment-desc">깊은 탄력과 리프팅을 동시에</p> -->
|
||||
<p class="equipment-price"><!-- 600,000<span>부터</span> --></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="equipment-card">
|
||||
<div class="equipment-image">
|
||||
<img src="/image/equip/온다리프팅.jpg" alt="온다리프팅">
|
||||
</div>
|
||||
<div class="equipment-info">
|
||||
<h3 class="equipment-name">온다리프팅</h3>
|
||||
<!-- <p class="equipment-desc">깊은 탄력과 리프팅을 동시에</p> -->
|
||||
<!-- <p class="equipment-price">165,000<span>부터</span></p> -->
|
||||
</div>
|
||||
</div>
|
||||
<div class="equipment-card">
|
||||
<div class="equipment-image">
|
||||
<img src="/image/equip/튠바디.jpg" alt="튠바디">
|
||||
</div>
|
||||
<div class="equipment-info">
|
||||
<h3 class="equipment-name">튠바디</h3>
|
||||
<!-- <p class="equipment-desc">깊은 탄력과 리프팅을 동시에</p> -->
|
||||
<p class="equipment-price"><!-- 150,000<span>부터</span> --></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 2번째 행 -->
|
||||
<div class="equipment-card">
|
||||
<div class="equipment-image">
|
||||
<img src="/image/equip/튠페이스.jpg" alt="튠페이스">
|
||||
</div>
|
||||
<div class="equipment-info">
|
||||
<h3 class="equipment-name">튠페이스</h3>
|
||||
<!-- <p class="equipment-desc">깊은 탄력과 리프팅을 동시에</p> -->
|
||||
<p class="equipment-price"><!-- 400,000<span>부터</span> --></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="equipment-card">
|
||||
<div class="equipment-image">
|
||||
<img src="/image/equip/울핏.jpg" alt="울핏">
|
||||
</div>
|
||||
<div class="equipment-info">
|
||||
<h3 class="equipment-name">울핏</h3>
|
||||
<!-- <p class="equipment-desc">깊은 탄력과 리프팅을 동시에</p> -->
|
||||
<p class="equipment-price"><!-- 50,000<span>부터</span> --></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="equipment-card">
|
||||
<div class="equipment-image">
|
||||
<img src="/image/equip/포텐자.jpg" alt="포텐자">
|
||||
</div>
|
||||
<div class="equipment-info">
|
||||
<h3 class="equipment-name">포텐자</h3>
|
||||
<!-- <p class="equipment-desc">깊은 탄력과 리프팅을 동시에</p> -->
|
||||
<p class="equipment-price"><!-- 150,000<span>부터</span> --></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="equipment-card">
|
||||
<div class="equipment-image">
|
||||
<img src="/image/equip/인모드.jpg" alt="인모드">
|
||||
</div>
|
||||
<div class="equipment-info">
|
||||
<h3 class="equipment-name">인모드</h3>
|
||||
<!-- <p class="equipment-desc">깊은 탄력과 리프팅을 동시에</p> -->
|
||||
<p class="equipment-price"><!-- 100,000<span>부터</span> --></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 3번째 행 -->
|
||||
<div class="equipment-card">
|
||||
<div class="equipment-image">
|
||||
<img src="/image/equip/슈링크유니버스.jpg" alt="슈링크유니버스">
|
||||
</div>
|
||||
<div class="equipment-info">
|
||||
<h3 class="equipment-name">슈링크유니버스</h3>
|
||||
<!-- <p class="equipment-desc">깊은 탄력과 리프팅을 동시에</p> -->
|
||||
<p class="equipment-price"><!-- 89,000<span>부터</span> --></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="equipment-card">
|
||||
<div class="equipment-image">
|
||||
<img src="/image/equip/바디고주파테라피.jpg" alt="바디고주파테라피">
|
||||
</div>
|
||||
<div class="equipment-info">
|
||||
<h3 class="equipment-name">바디고주파테라피</h3>
|
||||
<!-- <p class="equipment-desc">깊은 탄력과 리프팅을 동시에</p> -->
|
||||
<p class="equipment-price"><!-- 100,000<span>부터</span> --></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="equipment-card">
|
||||
<div class="equipment-image">
|
||||
<img src="/image/equip/리포덤.jpg" alt="리포덤">
|
||||
</div>
|
||||
<div class="equipment-info">
|
||||
<h3 class="equipment-name">리포덤</h3>
|
||||
<!-- <p class="equipment-desc">깊은 탄력과 리프팅을 동시에</p> -->
|
||||
<p class="equipment-price"><!-- 50,000<span>부터</span> --></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="content4">
|
||||
<div class="inner_wrap">
|
||||
<p>
|
||||
한 공간에서 고객 한분 한분께 자연스러운 아름다움과<br class="mb"/> 건강한 다이어트를 위해 항상 노력하는<br/>
|
||||
한 공간에서 고객 한분 한분께 자연스러운 아름다움과<br class="mb" /> 건강한 다이어트를 위해 항상 노력하는<br />
|
||||
<span>Total Beauty One Stop System</span>
|
||||
</p>
|
||||
<ul>
|
||||
@@ -229,56 +227,57 @@
|
||||
<div class="text_box">
|
||||
<p>
|
||||
<span>
|
||||
자연스러운 아름다움과<br/>
|
||||
자연스러운 아름다움과<br />
|
||||
건강한 다이어트
|
||||
</span>
|
||||
</p>
|
||||
<p>
|
||||
메이드유는 <span>Total Beauty One Stop System</span>으로<br class="pc"/>
|
||||
한 공간에서 고객 한분 <br class="mb"/>한분께 자연스러운 아름다움과<br/>
|
||||
건강한 다이어트를 위해 항상 노력하는 함께하는 조언자가<br class="pc"/>
|
||||
메이드유는 <span>Total Beauty One Stop System</span>으로<br class="pc" />
|
||||
한 공간에서 고객 한분 <br class="mb" />한분께 자연스러운 아름다움과<br />
|
||||
건강한 다이어트를 위해 항상 노력하는 함께하는 조언자가<br class="pc" />
|
||||
되어 드릴 것을 약속드립니다.
|
||||
</p>
|
||||
</div>
|
||||
<img class="pc" src="/image/web/introduction_content4-1.jpg" alt="introduction_content4-1"/>
|
||||
<img class="mb" src="/image/web/Mintroduction_content4-1.jpg" alt="introduction_content4-1"/>
|
||||
<img class="pc" src="/image/web/introduction_content4-1.jpg" alt="introduction_content4-1" />
|
||||
<img class="mb" src="/image/web/Mintroduction_content4-1.jpg" alt="introduction_content4-1" />
|
||||
</li>
|
||||
<li class="red_box">
|
||||
<div class="text_box">
|
||||
<p>
|
||||
<span>고객을 가족으로 생각하는 마음으로<br class="pc"/>정직한 시술, <br class="mb"/>만족할 수 있는 결과,</span><br/>
|
||||
더 나은 감동을 선사하기 위해<br/>
|
||||
<span>고객을 가족으로 생각하는 마음으로<br class="pc" />정직한 시술, <br class="mb" />만족할 수 있는
|
||||
결과,</span><br />
|
||||
더 나은 감동을 선사하기 위해<br />
|
||||
끊임없이 노력하겠습니다.
|
||||
</p>
|
||||
<p>
|
||||
메이드유는 <span>전국, 해외에서 찾아오는 비만센터와<br class="pc"/> 프리미멈 명품
|
||||
장비 보유 및 장비 최다 보유</span>와 엘란쎄, <br class="pc"/>스컬트라 콜라겐 볼륨
|
||||
전국 3대 병원인 쁘띠 센터로 <br class="pc"/>구분되어 고객에 맞춰 운영되고
|
||||
메이드유는 <span>전국, 해외에서 찾아오는 비만센터와<br class="pc" /> 프리미멈 명품
|
||||
장비 보유 및 장비 최다 보유</span>와 엘란쎄, <br class="pc" />스컬트라 콜라겐 볼륨
|
||||
전국 3대 병원인 쁘띠 센터로 <br class="pc" />구분되어 고객에 맞춰 운영되고
|
||||
있습니다.
|
||||
</p>
|
||||
</div>
|
||||
<img class="pc" src="/image/web/introduction_content4-2.jpg" alt="introduction_content4-2"/>
|
||||
<img class="mb" src="/image/web/Mintroduction_content4-2.jpg" alt="introduction_content4-2"/>
|
||||
<img class="pc" src="/image/web/introduction_content4-2.jpg" alt="introduction_content4-2" />
|
||||
<img class="mb" src="/image/web/Mintroduction_content4-2.jpg" alt="introduction_content4-2" />
|
||||
</li>
|
||||
<li>
|
||||
<div class="text_box">
|
||||
<p>
|
||||
No Pain 통증 없이<br/>
|
||||
No Bruise 멍 없이<br/>
|
||||
No Swelling 붓기 없이<br/>
|
||||
No Pain 통증 없이<br />
|
||||
No Bruise 멍 없이<br />
|
||||
No Swelling 붓기 없이<br />
|
||||
<span>3No 의료서비스를 지향</span>
|
||||
</p>
|
||||
<p>
|
||||
모든 제품은 정품, 정량, 정품 장비 사용을 원칙으로 안전을 <br class="pc"/>
|
||||
최우선으로 하고 있으며, 모든 시술은 <span>No Pain, No Bruise, <br class="pc"/>
|
||||
No Swelling라는 3No 의료서비스를 지향</span>합니다. <br class="pc"/>
|
||||
앞으로도 고품질의 관리와 서비스를 받을 수 있도록 노력하며 <br class="pc"/>
|
||||
사소한 불편까지 읽어주는 세심한 배려, 마음까지 읽는 서비스로<br class="pc"/>
|
||||
모든 제품은 정품, 정량, 정품 장비 사용을 원칙으로 안전을 <br class="pc" />
|
||||
최우선으로 하고 있으며, 모든 시술은 <span>No Pain, No Bruise, <br class="pc" />
|
||||
No Swelling라는 3No 의료서비스를 지향</span>합니다. <br class="pc" />
|
||||
앞으로도 고품질의 관리와 서비스를 받을 수 있도록 노력하며 <br class="pc" />
|
||||
사소한 불편까지 읽어주는 세심한 배려, 마음까지 읽는 서비스로<br class="pc" />
|
||||
무한 감동을 드릴 것을 약속합니다.
|
||||
</p>
|
||||
</div>
|
||||
<img class="pc" src="/image/web/introduction_content4-3.jpg" alt="introduction_content4-3"/>
|
||||
<img class="mb" src="/image/web/Mintroduction_content4-3.jpg" alt="introduction_content4-3"/>
|
||||
<img class="pc" src="/image/web/introduction_content4-3.jpg" alt="introduction_content4-3" />
|
||||
<img class="mb" src="/image/web/Mintroduction_content4-3.jpg" alt="introduction_content4-3" />
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
@@ -287,4 +286,5 @@
|
||||
</th:block>
|
||||
<th:block layout:fragment="layoutContentScript">
|
||||
</th:block>
|
||||
|
||||
</html>
|
||||
@@ -34,9 +34,9 @@
|
||||
<li>
|
||||
<img src="/image/web/profile1.jpg" alt="profile"/>
|
||||
<div class="text_box">
|
||||
<p class="field">
|
||||
<!--<p class="field">
|
||||
<span>비만</span>
|
||||
</p>
|
||||
</p>-->
|
||||
<p class="name">
|
||||
<span class="name">박재우</span>
|
||||
대표원장 / <span>Chief Medical Director</span>
|
||||
@@ -45,14 +45,13 @@
|
||||
<span>Profile</span><br/>
|
||||
현) 메이드유의원 프랜차이즈 대표원장 <br/>
|
||||
현) 메이드유의원 강남본점 대표원장 <br/>
|
||||
현) 메이드유의원 비만센터 대표원장 <br/>
|
||||
대한미용외과학회 회원 <br/>
|
||||
대한미용성형레이저의학회 회원 <br/>
|
||||
대한비만치료학회 회원
|
||||
</p>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<!--<li>
|
||||
<img src="/image/web/profile2.png" alt="profile"/>
|
||||
<div class="text_box">
|
||||
<p class="field">
|
||||
@@ -74,7 +73,7 @@
|
||||
한국미용성형학회 회원
|
||||
</p>
|
||||
</div>
|
||||
</li>
|
||||
</li>-->
|
||||
<!-- <li> 2025.09.03 요구사항으로 인한 주석처리 byPJS
|
||||
<img src="/image/web/profile3.png" alt="profile"/>
|
||||
<div class="text_box">
|
||||
@@ -98,7 +97,7 @@
|
||||
</p>
|
||||
</div>
|
||||
</li> -->
|
||||
<li>
|
||||
<!--<li>
|
||||
<img src="/image/web/profile4.png" alt="profile"/>
|
||||
<div class="text_box">
|
||||
<p class="field">
|
||||
@@ -120,7 +119,7 @@
|
||||
대한미용성형레이저의학회 회원
|
||||
</p>
|
||||
</div>
|
||||
</li>
|
||||
</li>-->
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -3,10 +3,21 @@
|
||||
xmlns:th="http://www.thymeleaf.org"
|
||||
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
|
||||
<head>
|
||||
<!-- Google Tag Manager -->
|
||||
<script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
|
||||
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
|
||||
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
|
||||
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
|
||||
})(window,document,'script','dataLayer','GTM-N4SDGT5N');</script>
|
||||
<!-- End Google Tag Manager -->
|
||||
<th:block th:replace="/web/layout/layoutHead :: layoutHead"></th:block>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<!-- Google Tag Manager (noscript) -->
|
||||
<noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-N4SDGT5N"
|
||||
height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
|
||||
<!-- End Google Tag Manager (noscript) -->
|
||||
<!-- 상단 스크립트 타임리프 변수 선언 -->
|
||||
<th:block layout:fragment="layout_top_script"></th:block>
|
||||
|
||||
|
||||
@@ -5,8 +5,7 @@
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
<meta name="keyword" content="HTML, meta, tag, element, reference">
|
||||
<meta name="author" content="NTSOFT">
|
||||
<meta name="description" content={props.description} data-react-helmet="true" />
|
||||
<meta name="author" content="VARASOFT">
|
||||
|
||||
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8" />
|
||||
<meta http-equiv="Cache-Control" content="No-Cache" />
|
||||
@@ -15,17 +14,15 @@
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=EmulateIE10">
|
||||
|
||||
<!-- sns미리보기 -->
|
||||
<meta property="og:type" content="website">
|
||||
<!-- <meta property="og:url" content="https://ntsoft.kr/"> -->
|
||||
<meta property="og:image" content="">
|
||||
<meta property="og:title" content="메이드유">
|
||||
<meta property="og:description" content="설명문구">
|
||||
|
||||
<!-- 사이트등록및소유확인 -->
|
||||
<meta name="naver-site-verification" content="" />
|
||||
<meta name="naver-site-verification" content="194ca1bb5bf1c69ced9c4c9693bef1adacb4acd4" />
|
||||
|
||||
<title>메이드유</title>
|
||||
<meta name="description" content="강남 다이어트 전문 메이드유 강남본점, 1:1 맞춤 프로그램으로 건강한 체중감량과 요요 관리까지 체계적으로 진행합니다.고민을 정교하게 해결해드립니다.">
|
||||
<meta property="og:type" content="website">
|
||||
<meta property="og:title" content="메이드유 강남본점ㅣ강남다이어트병원">
|
||||
<meta name="og:description" content="강남 다이어트 전문 메이드유 강남본점, 1:1 맞춤 프로그램으로 건강한 체중감량과 요요 관리까지 체계적으로 진행합니다.고민을 정교하게 해결해드립니다.">
|
||||
|
||||
<title>메이드유 강남본점ㅣ강남다이어트병원</title>
|
||||
|
||||
<!-- 파비콘 -->
|
||||
<link rel="shortcut icon" href="" type="image/x-icon">
|
||||
|
||||
@@ -1,99 +1,114 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ko" xmlns:th="http://www.thymeleaf.org">
|
||||
<th:block th:fragment="layoutHeader">
|
||||
<th:block th:replace="/web/layout/layoutModal :: layoutModal"></th:block>
|
||||
<link rel="stylesheet" href="/css/web/quick_menu/quick_menu.css">
|
||||
<script>
|
||||
// 디바이스 감지 함수
|
||||
function isMobileDevice() {
|
||||
return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)
|
||||
|| window.innerWidth <= 768;
|
||||
}
|
||||
function movePetitCenter(){
|
||||
window.location.href="https://petit.madeu.co.kr/index";
|
||||
}
|
||||
// 카카오톡 상담 함수 (모든 디바이스에서 동작)
|
||||
function openKakaoTalk() {
|
||||
window.open('https://pf.kakao.com/_puZExd', '_blank');
|
||||
}
|
||||
|
||||
// 전화걸기 함수 (모바일에서만 동작)
|
||||
function makePhoneCall() {
|
||||
if (isMobileDevice()) {
|
||||
// 모바일에서만 전화걸기
|
||||
window.location.href = 'tel:02-547-4711';
|
||||
} else {
|
||||
// 데스크톱에서는 동작 안함 (필요시 알림 추가)
|
||||
console.log('전화걸기는 모바일에서만 가능합니다.');
|
||||
}
|
||||
}
|
||||
// 모바일 여부 확인 함수
|
||||
function isMobile() {
|
||||
return window.innerWidth <= 768; // 필요 시 기준 너비 조정
|
||||
}
|
||||
<th:block th:replace="/web/layout/layoutModal :: layoutModal"></th:block>
|
||||
<link rel="stylesheet" href="/css/web/quick_menu/quick_menu.css">
|
||||
<script>
|
||||
// 디바이스 감지 함수
|
||||
function isMobileDevice() {
|
||||
return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)
|
||||
|| window.innerWidth <= 768;
|
||||
}
|
||||
function movePetitCenter() {
|
||||
window.location.href = "https://petit.madeu.co.kr/index";
|
||||
}
|
||||
// 카카오톡 상담 함수 (모든 디바이스에서 동작)
|
||||
function openKakaoTalk() {
|
||||
window.open('https://pf.kakao.com/_puZExd', '_blank');
|
||||
}
|
||||
|
||||
// 현재 경로가 /index인지 확인
|
||||
function isIndexPage() {
|
||||
return window.location.pathname === '/index'; // '/index.html'인 경우 조정 필요
|
||||
}
|
||||
function moveEvent() {
|
||||
window.location.href = "https://diet.madeu.co.kr/webevent/selectListWebEventIntro.do";
|
||||
}
|
||||
|
||||
// 퀵메뉴 표시/숨김 함수
|
||||
function toggleQuickMenu() {
|
||||
const quickMenu = document.querySelector('.quick-menu-simple');
|
||||
if (!quickMenu) return; // 요소가 없으면 종료
|
||||
function moveReview() {
|
||||
window.location.href = "https://diet.madeu.co.kr/webreview/selectListProcedureReviewIntro.do";
|
||||
}
|
||||
|
||||
if (isMobile()) {
|
||||
// 모바일: /index일 때만 표시
|
||||
quickMenu.style.display = isIndexPage() ? '' : 'none';
|
||||
} else {
|
||||
// 데스크톱: 항상 표시
|
||||
quickMenu.style.display = '';
|
||||
}
|
||||
}
|
||||
// 전화걸기 함수 (모바일에서만 동작)
|
||||
function makePhoneCall() {
|
||||
if (isMobileDevice()) {
|
||||
// 모바일에서만 전화걸기
|
||||
window.location.href = 'tel:02-547-4711';
|
||||
} else {
|
||||
// 데스크톱에서는 동작 안함 (필요시 알림 추가)
|
||||
console.log('전화걸기는 모바일에서만 가능합니다.');
|
||||
}
|
||||
}
|
||||
// 모바일 여부 확인 함수
|
||||
function isMobile() {
|
||||
return window.innerWidth <= 768; // 필요 시 기준 너비 조정
|
||||
}
|
||||
|
||||
// 페이지 로드와 리사이즈 이벤트 연결
|
||||
window.addEventListener('load', toggleQuickMenu);
|
||||
window.addEventListener('resize', toggleQuickMenu);
|
||||
</script>
|
||||
<header>
|
||||
<div class="inner_wrap">
|
||||
<button class="mb mbmenu" onClick="openNav()"></button>
|
||||
<a class="logo" href="/index"><img src="/image/logo_199x54.png" alt="logo"></a>
|
||||
<nav>
|
||||
<ul class="depth1">
|
||||
<li th:each="menu : ${menuList}">
|
||||
<a class="mmenu" th:href="${menu.menuUrl}" th:text="${menu.menuName}"></a>
|
||||
<ul class="depth2" th:if="${#lists.size(menu.children) > 0}">
|
||||
<li></li>
|
||||
<li th:each="subMenu : ${menu.children}">
|
||||
<a th:href="${subMenu.menuUrl}" th:text="${subMenu.menuName}"></a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
// 현재 경로가 /index인지 확인
|
||||
function isIndexPage() {
|
||||
return window.location.pathname === '/index'; // '/index.html'인 경우 조정 필요
|
||||
}
|
||||
|
||||
// 퀵메뉴 표시/숨김 함수
|
||||
function toggleQuickMenu() {
|
||||
const quickMenu = document.querySelector('.quick-menu-simple');
|
||||
if (!quickMenu) return; // 요소가 없으면 종료
|
||||
|
||||
if (isMobile()) {
|
||||
// 모바일: /index일 때만 표시
|
||||
quickMenu.style.display = isIndexPage() ? '' : 'none';
|
||||
} else {
|
||||
// 데스크톱: 항상 표시
|
||||
quickMenu.style.display = '';
|
||||
}
|
||||
}
|
||||
|
||||
// 페이지 로드와 리사이즈 이벤트 연결
|
||||
window.addEventListener('load', toggleQuickMenu);
|
||||
window.addEventListener('resize', toggleQuickMenu);
|
||||
</script>
|
||||
<header>
|
||||
<div class="inner_wrap">
|
||||
<button class="mb mbmenu" onClick="openNav()"></button>
|
||||
<a class="logo" href="/index"><img src="/image/logo_199x54.png" alt="logo"></a>
|
||||
<nav>
|
||||
<ul class="depth1">
|
||||
<li th:each="menu : ${menuList}">
|
||||
<a class="mmenu" th:href="${menu.menuUrl}" th:text="${menu.menuName}"></a>
|
||||
<ul class="depth2" th:if="${#lists.size(menu.children) > 0}">
|
||||
<li></li>
|
||||
<li th:each="subMenu : ${menu.children}">
|
||||
<a th:href="${subMenu.menuUrl}" th:text="${subMenu.menuName}"></a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
<!-- 퀵메뉴 -->
|
||||
<div class="quick-menu-simple">
|
||||
<div class="quick-item" onclick="moveReview()">
|
||||
<img src="/image/quick_menu/review.png" alt="고객후기">
|
||||
</div>
|
||||
<div class="quick-item" onclick="moveEvent()">
|
||||
<img src="/image/quick_menu/event.png" alt="이벤트">
|
||||
</div>
|
||||
<div class="quick-item" onclick="movePetitCenter()">
|
||||
<img src="/image/quick_menu/petit_center.png" alt="쁘띠센터">
|
||||
</div>
|
||||
<!-- 카카오톡 상담 -->
|
||||
<div class="quick-item kakao-consult" onclick="openKakaoTalk()">
|
||||
<img src="/image/quick_menu/kakao_consultation.png" alt="카카오톡 상담">
|
||||
</div>
|
||||
|
||||
<!-- 전화 상담 -->
|
||||
<div class="quick-item phone-consult" onclick="makePhoneCall()">
|
||||
<div class="phone-default">
|
||||
<img src="/image/quick_menu/call_consultation.png" alt="전화 상담">
|
||||
</div>
|
||||
<div class="phone-number">
|
||||
<img src="/image/quick_menu/madeu_phone_number.png" alt="전화 상담">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<!-- 퀵메뉴 -->
|
||||
<div class="quick-menu-simple">
|
||||
<div class="quick-item" onclick="movePetitCenter()">
|
||||
<img src="/image/quick_menu/petit_center.png" alt="쁘띠센터">
|
||||
</div>
|
||||
<!-- 카카오톡 상담 -->
|
||||
<div class="quick-item kakao-consult" onclick="openKakaoTalk()">
|
||||
<img src="/image/quick_menu/kakao_consultation.png" alt="카카오톡 상담">
|
||||
</div>
|
||||
|
||||
<!-- 전화 상담 -->
|
||||
<div class="quick-item phone-consult" onclick="makePhoneCall()">
|
||||
<div class="phone-default">
|
||||
<img src="/image/quick_menu/call_consultation.png" alt="전화 상담">
|
||||
</div>
|
||||
<div class="phone-number">
|
||||
<img src="/image/quick_menu/madeu_phone_number.png" alt="전화 상담">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script src="/js/web/layout/layoutHeader.js?ver=1"></script>
|
||||
<script src="/js/web/layout/layoutHeader.js?ver=1"></script>
|
||||
</th:block>
|
||||
|
||||
</html>
|
||||
@@ -28,8 +28,7 @@
|
||||
.container {
|
||||
max-width: 1280px;
|
||||
width: 100%;
|
||||
height: calc(100vh - 300px); /* 헤더/푸터 공간 확보 */
|
||||
min-height: calc(100vh - 300px);
|
||||
height: 100vh; /* 헤더/푸터 공간 확보 */
|
||||
margin: 0 auto;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
|
||||
@@ -1,432 +1,21 @@
|
||||
<!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"
|
||||
layout:decorate="~{/web/layout/layout}">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"
|
||||
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{/web/layout/layout}">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
<th:block layout:fragment="layoutCss">
|
||||
<!-- Choices.js CSS -->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/choices.js/10.2.0/choices.min.css" />
|
||||
|
||||
<style>
|
||||
* { box-sizing: border-box; }
|
||||
body {
|
||||
font-family: 'Noto Sans KR', sans-serif;
|
||||
margin: 0;
|
||||
background: #f7f7f9;
|
||||
color: #222;
|
||||
}
|
||||
#service-header {
|
||||
background: #fff;
|
||||
border-bottom: 1px solid #ececec;
|
||||
padding: 14px 0 14px 24px;
|
||||
font-size: 0.98em;
|
||||
color: #888;
|
||||
}
|
||||
.main-wrap {
|
||||
max-width: 1280px;
|
||||
margin: 0 auto;
|
||||
background: #fff;
|
||||
border-radius: 18px;
|
||||
box-shadow: 0 2px 12px rgba(0,0,0,0.07);
|
||||
margin-top: 32px;
|
||||
padding: 0 0 32px 0;
|
||||
}
|
||||
.top-section {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 32px;
|
||||
padding: 32px 32px 0 32px;
|
||||
align-items: flex-start;
|
||||
}
|
||||
.img-box {
|
||||
border-radius: 18px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
overflow: hidden;
|
||||
}
|
||||
.img-box img {
|
||||
width: 100%;
|
||||
object-fit: cover;
|
||||
border-radius: 18px;
|
||||
}
|
||||
.info-box {
|
||||
flex: 1 1 300px;
|
||||
min-width: 240px;
|
||||
}
|
||||
.info-title {
|
||||
font-size: 1.7em;
|
||||
font-weight: 700;
|
||||
margin-bottom: 6px;
|
||||
color: #222;
|
||||
}
|
||||
.info-desc {
|
||||
color: #444;
|
||||
font-size: 1.1em;
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
.info-price {
|
||||
font-size: 1.2em;
|
||||
font-weight: 700;
|
||||
color: #b23c3c;
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
.select-row {
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
.select-row label {
|
||||
font-weight: 500;
|
||||
margin-bottom: 8px;
|
||||
display: block;
|
||||
}
|
||||
.total-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
font-size: 1.1em;
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
.total-row .total-label {
|
||||
color: #888;
|
||||
}
|
||||
.total-row .total-price {
|
||||
font-weight: bold;
|
||||
color: #b23c3c;
|
||||
}
|
||||
.reserve-btn {
|
||||
width: 100%;
|
||||
padding: 14px 0;
|
||||
background: #b23c3c;
|
||||
color: #fff;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
font-size: 1.1em;
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
transition: background 0.15s;
|
||||
}
|
||||
.reserve-btn:disabled {
|
||||
background: #ddd;
|
||||
color: #888;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
.desc-section {
|
||||
margin-top: 36px;
|
||||
padding: 0 32px;
|
||||
}
|
||||
.desc-title {
|
||||
font-size: 1.25em;
|
||||
font-weight: 700;
|
||||
margin-bottom: 12px;
|
||||
color: #222;
|
||||
}
|
||||
.desc-content {
|
||||
color: #444;
|
||||
font-size: 1.05em;
|
||||
line-height: 1.7;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.hashtag-section {
|
||||
margin-top: 30px;
|
||||
padding: 20px 0;
|
||||
border-top: 1px solid #eee;
|
||||
}
|
||||
.hashtag-container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 0 20px;
|
||||
}
|
||||
.hashtag-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
}
|
||||
.hashtag {
|
||||
display: inline-block;
|
||||
background-color: #f8f9fa;
|
||||
color: #495057;
|
||||
padding: 8px 15px;
|
||||
border-radius: 20px;
|
||||
font-size: 14px;
|
||||
text-decoration: none;
|
||||
border: 1px solid #dee2e6;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
.hashtag:hover {
|
||||
background-color: #007bff;
|
||||
color: white;
|
||||
border-color: #007bff;
|
||||
cursor: pointer;
|
||||
}
|
||||
#thumbnail-bottom-txt{
|
||||
padding: 8px;
|
||||
margin-top: 10px;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
/* Choices.js 커스터마이징 - 개선된 버전 */
|
||||
.choices {
|
||||
border: 1px solid #ddd !important;
|
||||
border-radius: 6px !important;
|
||||
font-size: 1em !important;
|
||||
background: #fff !important;
|
||||
min-width: 300px !important;
|
||||
}
|
||||
|
||||
.choices__inner {
|
||||
background: #fff !important;
|
||||
padding: 8px 12px !important;
|
||||
min-height: 40px !important;
|
||||
color: #222 !important;
|
||||
min-width: 280px !important;
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
/* 플레이스홀더 텍스트 잘림 방지 - 핵심 수정 */
|
||||
.choices__placeholder {
|
||||
color: #888 !important;
|
||||
opacity: 1 !important;
|
||||
width: 100% !important;
|
||||
min-width: 200px !important;
|
||||
white-space: nowrap !important;
|
||||
overflow: visible !important;
|
||||
text-overflow: clip !important;
|
||||
display: block !important;
|
||||
}
|
||||
|
||||
.choices__input {
|
||||
color: #222 !important;
|
||||
background: transparent !important;
|
||||
min-width: 200px !important;
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
.choices__input::placeholder {
|
||||
color: #888 !important;
|
||||
opacity: 1 !important;
|
||||
width: 100% !important;
|
||||
min-width: 200px !important;
|
||||
white-space: nowrap !important;
|
||||
overflow: visible !important;
|
||||
}
|
||||
|
||||
/* 다중 선택시 입력 필드 너비 확보 */
|
||||
.choices[data-type*="select-multiple"] .choices__input {
|
||||
min-width: 200px !important;
|
||||
width: auto !important;
|
||||
flex: 1 !important;
|
||||
}
|
||||
|
||||
/* 선택된 항목들과 입력 필드 공간 분배 */
|
||||
.choices__list--multiple {
|
||||
/* display: flex !important; */
|
||||
flex-wrap: wrap !important;
|
||||
align-items: center !important;
|
||||
}
|
||||
|
||||
.choices__list--multiple:empty + .choices__input {
|
||||
min-width: 200px !important;
|
||||
width: 100% !important;
|
||||
flex: 1 !important;
|
||||
}
|
||||
|
||||
/* 선택된 항목의 가격 표시 스타일 - 새로 추가 */
|
||||
.selected-item-content {
|
||||
display: flex !important;
|
||||
flex-direction: column !important;
|
||||
align-items: flex-start !important;
|
||||
flex: 1 !important;
|
||||
}
|
||||
|
||||
.selected-item-name {
|
||||
font-weight: 500 !important;
|
||||
margin-bottom: 2px !important;
|
||||
}
|
||||
|
||||
.selected-item-price {
|
||||
font-size: 0.85em !important;
|
||||
}
|
||||
|
||||
.selected-price {
|
||||
color: #b23c3c !important;
|
||||
font-weight: bold !important;
|
||||
}
|
||||
|
||||
.selected-price-discount {
|
||||
color: #b23c3c !important;
|
||||
font-weight: bold !important;
|
||||
font-size: 0.9em !important;
|
||||
}
|
||||
|
||||
.selected-price-original {
|
||||
color: #aaa !important;
|
||||
font-size: 0.85em !important;
|
||||
text-decoration: line-through !important;
|
||||
margin-left: 4px !important;
|
||||
}
|
||||
|
||||
/* 선택된 항목들 스타일 수정 */
|
||||
.choices__list--multiple .choices__item {
|
||||
background-color: #f8f9fa !important;
|
||||
border: 1px solid #dee2e6 !important;
|
||||
border-radius: 16px !important;
|
||||
padding: 8px 12px !important;
|
||||
margin: 2px !important;
|
||||
font-size: 0.9em !important;
|
||||
color: #495057 !important;
|
||||
flex-shrink: 0 !important;
|
||||
display: flex !important;
|
||||
align-items: center !important;
|
||||
/* max-width: 300px !important; */
|
||||
}
|
||||
|
||||
/* 빨간색으로 변경 */
|
||||
.choices__button {
|
||||
background-image: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjEiIGhlaWdodD0iMjEiIHZpZXdCb3g9IjAgMCAyMSAyMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48ZyBmaWxsPSIjYjIzYzNjIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPjxwYXRoIGQ9Ik0yLjU5Mi4wNDRsMTguMzY0IDE4LjM2NC0yLjU0OCAyLjU0OEwuMDQ0IDIuNTkyeiIvPjxwYXRoIGQ9Ik0wIDE4LjM2NEwxOC4zNjQgMGwyLjU0OCAyLjU0OEwyLjU0OCAyMC45MTJ6Ii8+PC9nPjwvc3ZnPg==') !important;
|
||||
background-size: 14px 14px !important;
|
||||
background-position: center !important;
|
||||
background-repeat: no-repeat !important;
|
||||
border-left:0 !important;
|
||||
margin:0 !important;
|
||||
padding:0 !important;
|
||||
width:14px !important;
|
||||
}
|
||||
|
||||
/* 드롭다운 컨테이너 */
|
||||
.choices__list--dropdown {
|
||||
background: #fff !important;
|
||||
border: 1px solid #ddd !important;
|
||||
border-radius: 6px !important;
|
||||
box-shadow: 0 2px 8px rgba(0,0,0,0.1) !important;
|
||||
}
|
||||
|
||||
/* 드롭다운 옵션들 스타일 */
|
||||
.choices__list--dropdown .choices__item {
|
||||
padding: 8px 12px !important;
|
||||
display: flex !important;
|
||||
justify-content: space-between !important;
|
||||
align-items: center !important;
|
||||
color: #222 !important;
|
||||
}
|
||||
|
||||
.choices__list--dropdown .choices__item--selectable:hover {
|
||||
background-color: #f5f5f5 !important;
|
||||
color: #222 !important;
|
||||
}
|
||||
|
||||
.choices__list--dropdown .choices__item--highlighted {
|
||||
background-color: #b23c3c !important;
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
/* 포커스 상태 */
|
||||
.choices.is-focused .choices__inner {
|
||||
border-color: #b23c3c !important;
|
||||
}
|
||||
|
||||
/* 가격 표시 스타일 */
|
||||
.procedure-price {
|
||||
color: #b23c3c !important;
|
||||
font-weight: bold !important;
|
||||
font-size: 0.9em !important;
|
||||
}
|
||||
|
||||
.procedure-price-discount {
|
||||
color: #b23c3c !important;
|
||||
font-weight: bold !important;
|
||||
font-size: 0.9em !important;
|
||||
}
|
||||
|
||||
.procedure-price-original {
|
||||
color: #aaa !important;
|
||||
font-size: 0.85em !important;
|
||||
text-decoration: line-through !important;
|
||||
margin-left: 4px !important;
|
||||
}
|
||||
|
||||
/* 드롭다운이 열렸을 때 하이라이트된 항목의 가격 색상 */
|
||||
.choices__list--dropdown .choices__item--highlighted .procedure-price,
|
||||
.choices__list--dropdown .choices__item--highlighted .procedure-price-discount {
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
.choices__list--dropdown .choices__item--highlighted .procedure-price-original {
|
||||
color: #ddd !important;
|
||||
}
|
||||
|
||||
/* 검색 입력창 스타일 */
|
||||
.choices[data-type*="select-multiple"] .choices__input {
|
||||
background-color: transparent !important;
|
||||
color: #222 !important;
|
||||
}
|
||||
|
||||
/* 비활성화 상태 */
|
||||
.choices.is-disabled .choices__inner {
|
||||
background-color: #f8f9fa !important;
|
||||
color: #6c757d !important;
|
||||
cursor: not-allowed !important;
|
||||
}
|
||||
|
||||
/* 반응형 대응 */
|
||||
@media (max-width: 600px) {
|
||||
.choices {
|
||||
min-width: 250px !important;
|
||||
}
|
||||
|
||||
.choices__inner {
|
||||
min-width: 230px !important;
|
||||
}
|
||||
|
||||
.choices__placeholder,
|
||||
.choices__input,
|
||||
.choices__input::placeholder {
|
||||
min-width: 150px !important;
|
||||
}
|
||||
|
||||
.choices__list--multiple .choices__item {
|
||||
max-width: 250px !important;
|
||||
padding: 6px 10px !important;
|
||||
}
|
||||
|
||||
.selected-item-name {
|
||||
font-size: 0.9em !important;
|
||||
}
|
||||
|
||||
.selected-item-price {
|
||||
font-size: 0.8em !important;
|
||||
}
|
||||
|
||||
.choices__list--multiple .choices__item[data-deletable] .choices__button {
|
||||
width: 20px !important;
|
||||
height: 20px !important;
|
||||
font-size: 16px !important;
|
||||
text-indent: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.main-wrap { margin-top: 16px; }
|
||||
.top-section { flex-direction: column; gap: 18px; padding: 20px 10px 0 10px; }
|
||||
.img-box { width: 100%;}
|
||||
.info-box { min-width: unset; }
|
||||
.desc-section { padding: 0 10px; }
|
||||
}
|
||||
@media (max-width: 600px) {
|
||||
.main-wrap { margin-top: 0; border-radius: 0; box-shadow: none; }
|
||||
.top-section { padding: 12px 2vw 0 2vw; }
|
||||
.desc-section { padding: 0 2vw; }
|
||||
}
|
||||
</style>
|
||||
<!-- Choices.js CSS -->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/choices.js/10.2.0/choices.min.css" />
|
||||
<link rel="stylesheet" th:href="@{/css/web/webevent/webEventSelect.css}" />
|
||||
</th:block>
|
||||
|
||||
<th:block layout:fragment="layout_top_script">
|
||||
<script th:inline="javascript">
|
||||
let category_div_cd = [[${CATEGORY_DIV_CD}]];
|
||||
let category_no = [[${CATEGORY_NO}]];
|
||||
let post_no = [[${POST_NO}]];
|
||||
const CDN_URL = [[${@environment.getProperty('url.cdn')}]];
|
||||
</script>
|
||||
<script th:inline="javascript">
|
||||
let category_div_cd = [[${ CATEGORY_DIV_CD }]];
|
||||
let category_no = [[${ CATEGORY_NO }]];
|
||||
let post_no = [[${ POST_NO }]];
|
||||
const CDN_URL = [[${@environment.getProperty('url.cdn') }]];
|
||||
</script>
|
||||
</th:block>
|
||||
|
||||
<th:block layout:fragment="layoutContent">
|
||||
@@ -439,11 +28,11 @@ const CDN_URL = [[${@environment.getProperty('url.cdn')}]];
|
||||
<img id="serviceThumb" th:src="${@environment.getProperty('madeu.logo.size800x450')}" alt="썸네일 이미지">
|
||||
<pre id="thumbnail-bottom-txt"></pre>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="info-box">
|
||||
<div class="info-title" id="title">메쉬다 D/S</div>
|
||||
<div class="info-desc" id="contents">전신 라인 정리에 특화된 메쉬다 바디주사!</div>
|
||||
|
||||
|
||||
<div class="hashtag-section">
|
||||
<div class="hashtag-container">
|
||||
<div class="hashtag-list">
|
||||
@@ -458,14 +47,16 @@ const CDN_URL = [[${@environment.getProperty('url.cdn')}]];
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="event-period" id="event-period" style="display:none;"></div>
|
||||
|
||||
<div class="info-price"><span id="price">0</span>원 부터</div>
|
||||
|
||||
|
||||
<div class="select-row">
|
||||
<label for="procedure-select">시술 선택</label>
|
||||
<select id="procedure-select" multiple></select>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="total-row">
|
||||
<span class="total-label">총 금액</span>
|
||||
<span class="total-price" id="total">0원</span>
|
||||
@@ -473,7 +64,7 @@ const CDN_URL = [[${@environment.getProperty('url.cdn')}]];
|
||||
<button class="reserve-btn" id="reserve-btn" disabled>시술 예약하기</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="desc-section">
|
||||
<div class="desc-section">
|
||||
<!-- <div class="desc-content">
|
||||
<img id="contents_path" th:src="${@environment.getProperty('madeu.logo.size800x450')}" alt="컨텐츠 이미지" width="100%" />
|
||||
</div> -->
|
||||
@@ -482,301 +73,9 @@ const CDN_URL = [[${@environment.getProperty('url.cdn')}]];
|
||||
</th:block>
|
||||
|
||||
<th:block layout:fragment="layoutContentScript">
|
||||
<!-- Choices.js JavaScript -->
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/choices.js/10.2.0/choices.min.js"></script>
|
||||
|
||||
<script>
|
||||
// 전역 변수
|
||||
let procedureChoices;
|
||||
let priceList = [];
|
||||
const totalEl = document.getElementById('total');
|
||||
const reserveBtn = document.getElementById('reserve-btn');
|
||||
|
||||
// 초기화
|
||||
fn_SelectDetail(category_div_cd, category_no, post_no);
|
||||
|
||||
/****************************************************************************
|
||||
* 카테고리 목록 가져오기
|
||||
****************************************************************************/
|
||||
function fn_SelectDetail(category_div_cd, category_no, post_no) {
|
||||
let formData = new FormData();
|
||||
formData.append('CATEGORY_DIV_CD', category_div_cd);
|
||||
formData.append('CATEGORY_NO', category_no);
|
||||
formData.append('POST_NO', post_no);
|
||||
|
||||
$.ajax({
|
||||
url: encodeURI('/webevent/selectEventDetail.do'),
|
||||
data: formData,
|
||||
dataType: 'json',
|
||||
processData: false,
|
||||
contentType: false,
|
||||
type: 'POST',
|
||||
async: true,
|
||||
success: function(data) {
|
||||
if(data.msgCode == '0') {
|
||||
// 화면 데이터 변경
|
||||
$('#title').text(data.rows.TITLE);
|
||||
$('#serviceThumb').attr('src', CDN_URL + data.rows.THUMBNAIL_PATH);
|
||||
$('#thumbnail-bottom-txt').text(data.rows.THUMBNAIL_BOTTOM_TXT);
|
||||
$('#contents').text(data.rows.CONTENT);
|
||||
|
||||
if(data.rows.PRICE == null || data.rows.PRICE == undefined) {
|
||||
$('#price').text('0');
|
||||
} else {
|
||||
$('#price').text(data.rows.PRICE.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ","));
|
||||
}
|
||||
|
||||
// header 동적 변경
|
||||
$('#header-category-nm').text(data.rows.CATEGORY_NM);
|
||||
$('#header-title').text(data.rows.TITLE);
|
||||
|
||||
// 해시태그 처리
|
||||
var hashtagHtml = '';
|
||||
if (data.rows.HASHTAG) {
|
||||
var tags = data.rows.HASHTAG.split('#');
|
||||
tags.forEach(function(tag) {
|
||||
var trimmed = tag.trim();
|
||||
if(trimmed) {
|
||||
hashtagHtml += '<span class="hashtag">#' + trimmed + '</span>';
|
||||
}
|
||||
});
|
||||
}
|
||||
$('.hashtag-list').html(hashtagHtml);
|
||||
|
||||
$('#contents_path').attr('src', CDN_URL + data.rows.CONTENTS_PATH);
|
||||
|
||||
// 시술 목록 데이터 처리
|
||||
updateProcedureOptions(data.price || []);
|
||||
} else {
|
||||
modalEvent.danger("조회 오류", data.msgDesc);
|
||||
}
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
modalEvent.danger("조회 오류", "조회 중 오류가 발생하였습니다. 잠시후 다시시도하십시오.");
|
||||
},
|
||||
beforeSend: function() {
|
||||
$(".loading-image-layer").show();
|
||||
},
|
||||
complete: function() {
|
||||
$(".loading-image-layer").hide();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Choices.js 초기화 및 옵션 업데이트
|
||||
****************************************************************************/
|
||||
function updateProcedureOptions(data) {
|
||||
priceList = data;
|
||||
|
||||
// 기존 Choices 인스턴스 제거
|
||||
if (procedureChoices) {
|
||||
procedureChoices.destroy();
|
||||
}
|
||||
|
||||
// 선택 옵션 데이터 생성
|
||||
const choices = data.map(item => {
|
||||
if (!item.MU_TREATMENT_PROCEDURE_ID || !item.TREATMENT_PROCEDURE_NAME) return null;
|
||||
|
||||
const basePrice = (item.PRICE || 0) + (item.VAT || 0);
|
||||
const discountPrice = item.DISCOUNT_PRICE;
|
||||
|
||||
return {
|
||||
value: item.MU_TREATMENT_PROCEDURE_ID,
|
||||
label: item.TREATMENT_PROCEDURE_NAME,
|
||||
customProperties: {
|
||||
name: item.TREATMENT_PROCEDURE_NAME,
|
||||
price: basePrice,
|
||||
discountPrice: discountPrice,
|
||||
originalData: item
|
||||
}
|
||||
};
|
||||
}).filter(Boolean);
|
||||
|
||||
// Choices.js 초기화
|
||||
procedureChoices = new Choices('#procedure-select', {
|
||||
removeItemButton: true,
|
||||
searchEnabled: true,
|
||||
searchPlaceholderValue: '시술명으로 검색...',
|
||||
placeholder: true,
|
||||
placeholderValue: '시술을 선택하세요',
|
||||
maxItemCount: -1,
|
||||
choices: choices,
|
||||
shouldSort: false,
|
||||
searchResultLimit: 10,
|
||||
searchFields: ['label', 'value'],
|
||||
itemSelectText: '',
|
||||
noChoicesText: '선택 가능한 시술이 없습니다',
|
||||
noResultsText: '검색 결과가 없습니다',
|
||||
loadingText: '시술 정보를 불러오는 중...',
|
||||
|
||||
// 템플릿 커스터마이징 - 선택된 항목에 가격 표시 추가
|
||||
callbackOnCreateTemplates: function(template) {
|
||||
return {
|
||||
// 선택된 항목 템플릿 - 가격 정보 포함
|
||||
item: ({ classNames }, data) => {
|
||||
const customProps = data.customProperties || {};
|
||||
const name = customProps.name || data.label;
|
||||
const price = customProps.price || 0;
|
||||
const discountPrice = customProps.discountPrice;
|
||||
|
||||
// 가격 표시 HTML 생성
|
||||
let priceHtml = '';
|
||||
if (discountPrice && discountPrice < price) {
|
||||
priceHtml = `
|
||||
<span class="selected-price-discount">${discountPrice.toLocaleString()}원</span>
|
||||
<span class="selected-price-original">${price.toLocaleString()}원</span>
|
||||
`;
|
||||
} else {
|
||||
priceHtml = `<span class="selected-price">${price.toLocaleString()}원</span>`;
|
||||
}
|
||||
|
||||
return template(`
|
||||
<div class="${classNames.item} ${data.highlighted ? classNames.highlightedState : classNames.itemSelectable}" data-item data-id="${data.id}" data-value="${data.value}">
|
||||
<span class="selected-item-content">
|
||||
<span class="selected-item-name">${name}</span>
|
||||
<span class="selected-item-price">${priceHtml}</span>
|
||||
</span>
|
||||
<button type="button" class="${classNames.button}" aria-label="Remove item: '${data.value}'" data-button>X</button>
|
||||
|
||||
</div>
|
||||
`);
|
||||
},
|
||||
|
||||
// 드롭다운 선택 옵션 템플릿
|
||||
choice: ({ classNames }, data) => {
|
||||
const customProps = data.customProperties || {};
|
||||
const name = customProps.name || data.label;
|
||||
const price = customProps.price || 0;
|
||||
const discountPrice = customProps.discountPrice;
|
||||
|
||||
let priceHtml = '';
|
||||
if (discountPrice && discountPrice < price) {
|
||||
priceHtml = `
|
||||
<span class="procedure-price-discount">${discountPrice.toLocaleString()}원</span>
|
||||
<span class="procedure-price-original">${price.toLocaleString()}원</span>
|
||||
`;
|
||||
} else {
|
||||
priceHtml = `<span class="procedure-price">${price.toLocaleString()}원</span>`;
|
||||
}
|
||||
|
||||
return template(`
|
||||
<div class="${classNames.item} ${classNames.itemChoice} ${data.disabled ? classNames.itemDisabled : classNames.itemSelectable}" data-select-text="" data-choice ${data.disabled ? 'data-choice-disabled aria-disabled="true"' : 'data-choice-selectable'} data-id="${data.id}" data-value="${data.value}">
|
||||
<div style="display: flex; justify-content: space-between; width: 100%; align-items: center;">
|
||||
<span>${name}</span>
|
||||
${priceHtml}
|
||||
</div>
|
||||
</div>
|
||||
`);
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
// 이벤트 리스너 추가
|
||||
const selectElement = document.getElementById('procedure-select');
|
||||
selectElement.addEventListener('change', function(event) {
|
||||
console.log('Selection changed:', event.detail);
|
||||
updateTotalPrice();
|
||||
});
|
||||
|
||||
selectElement.addEventListener('addItem', function(event) {
|
||||
console.log('Item added:', event.detail);
|
||||
updateTotalPrice();
|
||||
});
|
||||
|
||||
selectElement.addEventListener('removeItem', function(event) {
|
||||
console.log('Item removed:', event.detail);
|
||||
updateTotalPrice();
|
||||
});
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* 총 금액 업데이트 - 개선된 버전
|
||||
****************************************************************************/
|
||||
function updateTotalPrice() {
|
||||
if (!procedureChoices) {
|
||||
console.log('procedureChoices not initialized');
|
||||
return;
|
||||
}
|
||||
|
||||
const selectedValues = procedureChoices.getValue(true);
|
||||
console.log('Selected values:', selectedValues);
|
||||
|
||||
let total = 0;
|
||||
|
||||
selectedValues.forEach(value => {
|
||||
const item = priceList.find(p => p.MU_TREATMENT_PROCEDURE_ID == value);
|
||||
console.log('Found item for value', value, ':', item);
|
||||
|
||||
if (item) {
|
||||
const basePrice = (item.PRICE || 0) + (item.VAT || 0);
|
||||
const discountPrice = item.DISCOUNT_PRICE;
|
||||
|
||||
// 할인가가 있고 더 저렴하면 할인가 사용, 아니면 기본가격 사용
|
||||
const finalPrice = (discountPrice && discountPrice < basePrice) ? discountPrice : basePrice;
|
||||
total += finalPrice;
|
||||
console.log('Added price:', finalPrice);
|
||||
}
|
||||
});
|
||||
|
||||
console.log('Total calculated:', total);
|
||||
totalEl.textContent = total.toLocaleString() + '원';
|
||||
reserveBtn.disabled = selectedValues.length === 0;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* 예약 페이지로 이동
|
||||
****************************************************************************/
|
||||
function fn_moveReservation(category_div, category_no, post_no) {
|
||||
if (!procedureChoices) {
|
||||
alert('시술 선택 기능이 초기화되지 않았습니다.');
|
||||
return;
|
||||
}
|
||||
|
||||
const selectedValues = procedureChoices.getValue(true);
|
||||
|
||||
if (selectedValues.length === 0) {
|
||||
alert('시술을 선택해주세요.');
|
||||
return;
|
||||
}
|
||||
|
||||
const form = document.createElement('form');
|
||||
form.method = 'post';
|
||||
form.action = '/webevent/selectMakeReservation.do';
|
||||
|
||||
// 기본 파라미터 추가
|
||||
const params = [
|
||||
{ name: 'CATEGORY_DIV_CD', value: category_div },
|
||||
{ name: 'CATEGORY_NO', value: category_no },
|
||||
{ name: 'POST_NO', value: post_no }
|
||||
];
|
||||
|
||||
params.forEach(param => {
|
||||
const input = document.createElement('input');
|
||||
input.type = 'hidden';
|
||||
input.name = param.name;
|
||||
input.value = param.value;
|
||||
form.appendChild(input);
|
||||
});
|
||||
|
||||
// 선택된 시술 추가
|
||||
selectedValues.forEach(value => {
|
||||
const input = document.createElement('input');
|
||||
input.type = 'hidden';
|
||||
input.name = 'PROCEDURE_ID';
|
||||
input.value = value;
|
||||
form.appendChild(input);
|
||||
});
|
||||
|
||||
document.body.appendChild(form);
|
||||
form.submit();
|
||||
}
|
||||
|
||||
// 예약 버튼 이벤트
|
||||
reserveBtn.addEventListener('click', function() {
|
||||
fn_moveReservation(category_div_cd, category_no, post_no);
|
||||
});
|
||||
</script>
|
||||
<!-- Choices.js JavaScript -->
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/choices.js/10.2.0/choices.min.js"></script>
|
||||
<script th:src="@{/js/web/webevent/webEventSelect.js}"></script>
|
||||
</th:block>
|
||||
</html>
|
||||
|
||||
</html>
|
||||
@@ -1,594 +1,56 @@
|
||||
<!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"
|
||||
layout:decorate="~{/web/layout/layout}">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"
|
||||
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{/web/layout/layout}">
|
||||
<th:block layout:fragment="layoutCss">
|
||||
<style>
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* 전체 기본 스타일 */
|
||||
html, body {
|
||||
height: 100vh;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-family: 'Pretendard', -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', sans-serif;
|
||||
background: #f8f9fa;
|
||||
color: #1a1a1a;
|
||||
overflow-x: hidden;
|
||||
font-size: 16px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
/* 메인 컨테이너 */
|
||||
.container {
|
||||
max-width: 1280px;
|
||||
width: 100%;
|
||||
margin: 0 auto;
|
||||
min-height: calc(100vh - 300px);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
/* 상단 헤더 영역 */
|
||||
.header {
|
||||
background: white;
|
||||
border-radius: 16px;
|
||||
box-shadow: 0 2px 16px rgba(0,0,0,0.06);
|
||||
padding: 1rem 2rem;
|
||||
border-bottom: 3px solid #C60B24;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.page-title {
|
||||
font-size: clamp(1.5rem, 3vw, 1.875rem); /* 24px ~ 30px */
|
||||
font-weight: 700;
|
||||
color: #1a1a1a;
|
||||
margin: 0;
|
||||
letter-spacing: -0.025em;
|
||||
}
|
||||
|
||||
/* 하단 콘텐츠 영역 (사이드바 + 메인) */
|
||||
.content-wrapper {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
gap: 1.5rem;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
/* 좌측 사이드바 */
|
||||
.sidebar {
|
||||
width: 240px;
|
||||
min-width: 220px;
|
||||
flex-shrink: 0;
|
||||
background: white;
|
||||
border-radius: 16px;
|
||||
box-shadow: 0 2px 16px rgba(0,0,0,0.06);
|
||||
height: fit-content;
|
||||
max-height: 100%;
|
||||
overflow-y: auto;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.sidebar-header {
|
||||
padding: 1.25rem 1.5rem;
|
||||
font-size: 1.125rem; /* 18px */
|
||||
font-weight: 700;
|
||||
color: #C60B24;
|
||||
border-bottom: 1px solid #e9ecef;
|
||||
}
|
||||
|
||||
.category-list {
|
||||
list-style: none;
|
||||
padding: 1rem;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.category-item {
|
||||
margin-bottom: 0.375rem;
|
||||
}
|
||||
|
||||
.category-link {
|
||||
display: block;
|
||||
width: 100%;
|
||||
padding: 0.75rem 1rem;
|
||||
font-size: 0.95rem; /* 15.2px */
|
||||
color: #6b7280;
|
||||
text-decoration: none;
|
||||
background: none;
|
||||
border: none;
|
||||
border-radius: 10px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.category-link:hover {
|
||||
background: #f9fafb;
|
||||
color: #C60B24;
|
||||
transform: translateX(2px);
|
||||
}
|
||||
|
||||
.category-link.active {
|
||||
background: rgba(198, 11, 36, 0.08);
|
||||
color: #C60B24;
|
||||
font-weight: 600;
|
||||
border-left: 4px solid #C60B24;
|
||||
}
|
||||
|
||||
/* 메인 콘텐츠 영역 */
|
||||
.main-content {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
/* 이벤트 리스트 */
|
||||
.event-list {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding-right: 0.5rem;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.event-grid {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1.25rem;
|
||||
padding-bottom: 2rem;
|
||||
}
|
||||
|
||||
/* 이벤트 카드 */
|
||||
.event-card {
|
||||
display: flex;
|
||||
background: white;
|
||||
border-radius: 16px;
|
||||
box-shadow: 0 2px 16px rgba(0,0,0,0.06);
|
||||
overflow: hidden;
|
||||
transition: all 0.3s ease;
|
||||
border: 1px solid #f1f5f9;
|
||||
min-height: 160px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.event-card:hover {
|
||||
transform: translateY(-4px);
|
||||
box-shadow: 0 8px 32px rgba(0,0,0,0.12);
|
||||
border-color: #C60B24;
|
||||
}
|
||||
|
||||
.event-img {
|
||||
width: 340px;
|
||||
min-width: 280px;
|
||||
background: #f3f4f6;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.event-img img {
|
||||
width: 100%;
|
||||
object-fit: cover;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.event-info {
|
||||
flex: 1;
|
||||
padding: 1.5rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.event-title {
|
||||
font-size: 1.25rem; /* 20px */
|
||||
font-weight: 700;
|
||||
margin-bottom: 0.5rem;
|
||||
color: #1a1a1a;
|
||||
letter-spacing: -0.025em;
|
||||
}
|
||||
|
||||
.event-desc {
|
||||
color: #6b7280;
|
||||
font-size: 0.9375rem; /* 15px */
|
||||
margin-bottom: 0.75rem;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.event-meta {
|
||||
font-size: 0.875rem; /* 14px */
|
||||
color: #9ca3af;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.event-price {
|
||||
font-size: 1.125rem; /* 18px */
|
||||
color: #C60B24;
|
||||
font-weight: 700;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
/* 로딩 및 에러 메시지 */
|
||||
.loading {
|
||||
text-align: center;
|
||||
padding: 2rem;
|
||||
color: #6b7280;
|
||||
font-size: 0.9375rem; /* 15px */
|
||||
}
|
||||
|
||||
.error-message {
|
||||
text-align: center;
|
||||
padding: 2rem;
|
||||
color: #dc2626;
|
||||
font-size: 0.9375rem; /* 15px */
|
||||
}
|
||||
|
||||
/* 스크롤바 커스터마이징 */
|
||||
.event-list::-webkit-scrollbar,
|
||||
.sidebar::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
|
||||
.event-list::-webkit-scrollbar-track,
|
||||
.sidebar::-webkit-scrollbar-track {
|
||||
background: #f8fafc;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.event-list::-webkit-scrollbar-thumb,
|
||||
.sidebar::-webkit-scrollbar-thumb {
|
||||
background: #cbd5e1;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.event-list::-webkit-scrollbar-thumb:hover,
|
||||
.sidebar::-webkit-scrollbar-thumb:hover {
|
||||
background: #C60B24;
|
||||
}
|
||||
|
||||
/* 반응형 디자인 - 태블릿 */
|
||||
@media (max-width: 1024px) {
|
||||
.container {
|
||||
padding: 1rem;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.content-wrapper {
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
width: 220px;
|
||||
min-width: 200px;
|
||||
}
|
||||
|
||||
.event-img {
|
||||
width: 220px;
|
||||
min-width: 220px;
|
||||
}
|
||||
|
||||
.event-img img {
|
||||
height: 140px;
|
||||
}
|
||||
|
||||
.header {
|
||||
padding: 1.25rem 1.5rem;
|
||||
}
|
||||
|
||||
.page-title {
|
||||
font-size: clamp(1.375rem, 2.8vw, 1.75rem); /* 22px ~ 28px */
|
||||
}
|
||||
}
|
||||
|
||||
/* 반응형 디자인 - 모바일 */
|
||||
@media (max-width: 768px) {
|
||||
.container {
|
||||
flex-direction: column;
|
||||
padding: 1rem;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.content-wrapper {
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
width: 100%;
|
||||
position: static;
|
||||
max-height: none;
|
||||
order: 1;
|
||||
}
|
||||
|
||||
.category-list {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
|
||||
gap: 0.5rem;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.category-link {
|
||||
text-align: center;
|
||||
padding: 0.75rem 0.5rem;
|
||||
font-size: 0.875rem; /* 14px */
|
||||
}
|
||||
|
||||
.main-content {
|
||||
order: 2;
|
||||
}
|
||||
|
||||
.event-list {
|
||||
overflow-y: visible;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.event-card {
|
||||
flex-direction: column;
|
||||
min-height: auto;
|
||||
}
|
||||
|
||||
.event-img {
|
||||
width: 100%;
|
||||
min-width: auto;
|
||||
}
|
||||
|
||||
.event-img img {
|
||||
height: 200px;
|
||||
}
|
||||
|
||||
.event-info {
|
||||
padding: 1.25rem;
|
||||
}
|
||||
|
||||
.header {
|
||||
padding: 1rem 1.5rem;
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
.page-title {
|
||||
font-size: clamp(1.25rem, 2.5vw, 1.5rem); /* 20px ~ 24px */
|
||||
}
|
||||
|
||||
.event-title {
|
||||
font-size: 1.125rem; /* 18px */
|
||||
}
|
||||
|
||||
.event-price {
|
||||
font-size: 1rem; /* 16px */
|
||||
}
|
||||
}
|
||||
|
||||
/* 작은 모바일 */
|
||||
@media (max-width: 480px) {
|
||||
.container {
|
||||
padding: 0.75rem;
|
||||
}
|
||||
|
||||
.header {
|
||||
padding: 0.875rem 1rem;
|
||||
}
|
||||
|
||||
.page-title {
|
||||
font-size: clamp(1.125rem, 2.2vw, 1.375rem); /* 18px ~ 22px */
|
||||
}
|
||||
|
||||
.event-info {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.event-title {
|
||||
font-size: 1rem; /* 16px */
|
||||
}
|
||||
|
||||
.event-desc {
|
||||
font-size: 0.875rem; /* 14px */
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<link rel="stylesheet" th:href="@{/css/web/webevent/webEventSelectList.css}" />
|
||||
</th:block>
|
||||
|
||||
<th:block layout:fragment="layout_top_script">
|
||||
<script th:inline="javascript">
|
||||
const CDN_URL = [[${@environment.getProperty('url.cdn')}]];
|
||||
</script>
|
||||
<script th:inline="javascript">
|
||||
const CDN_URL = [[${@environment.getProperty('url.cdn') }]];
|
||||
</script>
|
||||
</th:block>
|
||||
|
||||
<th:block layout:fragment="layoutContent">
|
||||
<div class="container">
|
||||
<!-- 상단 헤더 영역 -->
|
||||
<div class="header">
|
||||
<h1 class="page-title">이벤트 안내</h1>
|
||||
</div>
|
||||
|
||||
<!-- 하단 콘텐츠 영역 (사이드바 + 메인) -->
|
||||
<div class="content-wrapper">
|
||||
<!-- 좌측 사이드바 -->
|
||||
<aside class="sidebar">
|
||||
<div class="sidebar-header">이달의 이벤트</div>
|
||||
<ul class="category-list" id="category-list">
|
||||
<!-- 카테고리 JS로 렌더링 -->
|
||||
</ul>
|
||||
</aside>
|
||||
|
||||
<!-- 메인 콘텐츠 -->
|
||||
<main class="main-content">
|
||||
<div class="event-list">
|
||||
<div class="event-grid" id="event-grid">
|
||||
<!-- 이벤트 카드 JS로 렌더링 -->
|
||||
<div class="container">
|
||||
<!-- 상단 헤더 영역 -->
|
||||
<div class="header">
|
||||
<h1 class="page-title">이벤트 안내</h1>
|
||||
</div>
|
||||
|
||||
<!-- 하단 콘텐츠 영역 (사이드바 + 메인) -->
|
||||
<div class="content-wrapper">
|
||||
<!-- 좌측 사이드바 -->
|
||||
<aside class="sidebar">
|
||||
<ul class="category-list" id="category-list">
|
||||
<!-- 카테고리 JS로 렌더링 -->
|
||||
</ul>
|
||||
</aside>
|
||||
|
||||
<!-- 메인 콘텐츠 -->
|
||||
<main class="main-content">
|
||||
<div class="event-list">
|
||||
<div class="event-grid" id="event-grid">
|
||||
<!-- 이벤트 카드 JS로 렌더링 -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 지난 이벤트 레이어 팝업 -->
|
||||
<div class="popup-overlay" id="expired-popup">
|
||||
<div class="popup-content">
|
||||
<div class="popup-icon">⏰</div>
|
||||
<div class="popup-title">지난 이벤트</div>
|
||||
<div class="popup-message">이 이벤트는 종료되었습니다.<br>다른 진행 중인 이벤트를 확인해 주세요.</div>
|
||||
<button class="popup-close-btn" onclick="eventManager.hideExpiredPopup()">확인</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</th:block>
|
||||
|
||||
<th:block layout:fragment="layoutContentScript">
|
||||
<script>
|
||||
class EventManager {
|
||||
constructor() {
|
||||
this.events = [];
|
||||
this.categories = [];
|
||||
this.init();
|
||||
}
|
||||
|
||||
async init() {
|
||||
await this.loadCategories();
|
||||
}
|
||||
|
||||
async apiRequest(url, data) {
|
||||
return new Promise((resolve, reject) => {
|
||||
$.ajax({
|
||||
url: encodeURI(url),
|
||||
data: data,
|
||||
dataType: 'json',
|
||||
processData: false,
|
||||
contentType: false,
|
||||
type: 'POST',
|
||||
success: resolve,
|
||||
error: reject,
|
||||
beforeSend: () => $(".loading-image-layer").show(),
|
||||
complete: () => $(".loading-image-layer").hide()
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async loadCategories() {
|
||||
try {
|
||||
const formData = new FormData();
|
||||
formData.append('bannerType', 'A');
|
||||
const data = await this.apiRequest('/webevent/selectListWebEvent.do', formData);
|
||||
|
||||
if (data.msgCode === '0') {
|
||||
this.categories = data.rows;
|
||||
this.renderCategories();
|
||||
if (this.categories.length > 0) {
|
||||
this.loadEvents(this.categories[0].CATEGORY_NO);
|
||||
}
|
||||
} else {
|
||||
modalEvent.danger("조회 오류", data.msgDesc);
|
||||
}
|
||||
} catch (error) {
|
||||
modalEvent.danger("조회 오류", "조회 중 오류가 발생하였습니다.");
|
||||
}
|
||||
}
|
||||
|
||||
async loadEvents(categoryNo) {
|
||||
try {
|
||||
const formData = new FormData();
|
||||
formData.append('category_no', categoryNo);
|
||||
const data = await this.apiRequest('/webevent/selectListEvent.do', formData);
|
||||
|
||||
if (data.msgCode === '0') {
|
||||
this.events = data.rows.map(row => ({
|
||||
img: CDN_URL + row.THUMBNAIL_PATH,
|
||||
title: row.TITLE,
|
||||
desc: row.CONTENT,
|
||||
meta: row.THUMBNAIL_BOTTOM_TXT,
|
||||
price: {
|
||||
before: Number(row.PRICE) || 0,
|
||||
after: Number(row.DISCOUNT_PRICE) || 0
|
||||
},
|
||||
categoryDiv: row.CATEGORY_DIV_CD,
|
||||
categoryNo: row.CATEGORY_NO,
|
||||
postNo: row.POST_NO
|
||||
}));
|
||||
this.renderEvents();
|
||||
} else {
|
||||
modalEvent.danger("조회 오류", data.msgDesc);
|
||||
}
|
||||
} catch (error) {
|
||||
modalEvent.danger("조회 오류", "조회 중 오류가 발생하였습니다.");
|
||||
}
|
||||
}
|
||||
|
||||
renderCategories() {
|
||||
const html = this.categories.map((cat, idx) => `
|
||||
<li class="category-item">
|
||||
<a href="#" class="category-link ${idx === 0 ? 'active' : ''}"
|
||||
data-category="${cat.CATEGORY_NO}">${cat.CATEGORY_NM}</a>
|
||||
</li>
|
||||
`).join('');
|
||||
|
||||
document.getElementById('category-list').innerHTML = html;
|
||||
|
||||
document.querySelectorAll('.category-link').forEach(link => {
|
||||
link.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
document.querySelectorAll('.category-link').forEach(item =>
|
||||
item.classList.remove('active'));
|
||||
link.classList.add('active');
|
||||
this.loadEvents(link.dataset.category);
|
||||
document.querySelector('.event-list').scrollTop = 0;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
renderEvents() {
|
||||
const html = this.events.map(event => `
|
||||
<div class="event-card" data-category-div="${event.categoryDiv}"
|
||||
data-category-no="${event.categoryNo}" data-post-no="${event.postNo}">
|
||||
<div class="event-img">
|
||||
<img src="${event.img}" alt="${event.title}">
|
||||
</div>
|
||||
<div class="event-info">
|
||||
<div class="event-title">${event.title}</div>
|
||||
<div class="event-desc">${event.desc}</div>
|
||||
${event.meta ? `<div class="event-meta">${event.meta}</div>` : ''}
|
||||
<div class="event-price">
|
||||
<span style="text-decoration:line-through; color:#9ca3af; font-size:0.95em; margin-right:8px;">
|
||||
${event.price.before.toLocaleString()}원
|
||||
</span>
|
||||
${event.price.after.toLocaleString()}원 부터
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`).join('');
|
||||
|
||||
document.getElementById('event-grid').innerHTML = html;
|
||||
|
||||
// 카드 클릭 이벤트 추가
|
||||
document.querySelectorAll('.event-card').forEach(card => {
|
||||
card.addEventListener('click', () => {
|
||||
const categoryDiv = card.dataset.categoryDiv;
|
||||
const categoryNo = card.dataset.categoryNo;
|
||||
const postNo = card.dataset.postNo;
|
||||
this.goToDetail(categoryDiv, categoryNo, postNo);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
goToDetail(categoryDiv, categoryNo, postNo) {
|
||||
const form = document.createElement('form');
|
||||
form.method = 'post';
|
||||
form.action = '/webevent/selectEventDetailIntro.do';
|
||||
|
||||
const fields = [
|
||||
{ name: 'CATEGORY_DIV_CD', value: categoryDiv },
|
||||
{ name: 'CATEGORY_NO', value: categoryNo },
|
||||
{ name: 'POST_NO', value: postNo }
|
||||
];
|
||||
|
||||
fields.forEach(field => {
|
||||
const input = document.createElement('input');
|
||||
input.type = 'hidden';
|
||||
input.name = field.name;
|
||||
input.value = field.value;
|
||||
form.appendChild(input);
|
||||
});
|
||||
|
||||
document.body.appendChild(form);
|
||||
form.submit();
|
||||
}
|
||||
}
|
||||
|
||||
const eventManager = new EventManager();
|
||||
</script>
|
||||
<script th:src="@{/js/web/webevent/webEventSelectList.js}"></script>
|
||||
</th:block>
|
||||
</html>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,54 @@
|
||||
<!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" layout:decorate="~{/web/layout/layout}">
|
||||
<th:block layout:fragment="layoutCss">
|
||||
<link href="https://cdn.jsdelivr.net/npm/quill@2.0.3/dist/quill.snow.css" rel="stylesheet" />
|
||||
<link rel="stylesheet" th:href="@{/css/web/webreview/procedureReviewSelect.css}" />
|
||||
</th:block>
|
||||
|
||||
<th:block layout:fragment="layout_top_script">
|
||||
<script th:inline="javascript">
|
||||
const muProcedureReviewId = [[${ muProcedureReviewId }]];
|
||||
const CDN_URL = [[${@environment.getProperty('url.cdn') }]];
|
||||
</script>
|
||||
</th:block>
|
||||
|
||||
<th:block layout:fragment="layoutContent">
|
||||
<div class="container">
|
||||
<!-- 상단 네비게이션 -->
|
||||
<div class="breadcrumb">
|
||||
<a href="/webreview/selectListProcedureReviewIntro.do">고객후기</a>
|
||||
<span>></span>
|
||||
<span id="breadcrumb-title">상세보기</span>
|
||||
</div>
|
||||
|
||||
<!-- 게시글 컨텐츠 -->
|
||||
<article class="review-article">
|
||||
<div class="review-header">
|
||||
<h1 class="review-title" id="review-title"></h1>
|
||||
<div class="review-meta">
|
||||
<span class="review-date" id="review-date"></span>
|
||||
<span class="review-views">조회 <span id="review-views">0</span></span>
|
||||
</div>
|
||||
<div class="review-tags" id="review-tags"></div>
|
||||
</div>
|
||||
|
||||
<!-- Quill 에디터 본문 -->
|
||||
<div class="review-content" id="review-content">
|
||||
<div class="loading">게시글을 불러오는 중...</div>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<!-- 목록 버튼 -->
|
||||
<div class="btn-area">
|
||||
<a href="/webreview/selectListProcedureReviewIntro.do" class="btn-list">목록으로</a>
|
||||
</div>
|
||||
</div>
|
||||
</th:block>
|
||||
|
||||
<th:block layout:fragment="layoutContentScript">
|
||||
<script src="https://cdn.jsdelivr.net/npm/quill@2.0.3/dist/quill.js"></script>
|
||||
<script th:src="@{/js/web/webreview/procedureReviewSelect.js}"></script>
|
||||
</th:block>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,44 @@
|
||||
<!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" layout:decorate="~{/web/layout/layout}">
|
||||
<th:block layout:fragment="layoutCss">
|
||||
<link rel="stylesheet" th:href="@{/css/web/webreview/procedureReviewSelectList.css}" />
|
||||
</th:block>
|
||||
|
||||
<th:block layout:fragment="layout_top_script">
|
||||
<script th:inline="javascript">
|
||||
const CDN_URL = [[${@environment.getProperty('url.cdn') }]];
|
||||
</script>
|
||||
</th:block>
|
||||
|
||||
<th:block layout:fragment="layoutContent">
|
||||
<div class="container">
|
||||
<!-- 상단 헤더 -->
|
||||
<div class="header">
|
||||
<h1 class="page-title">고객후기</h1>
|
||||
<p class="page-subtitle">실제 고객님들의 생생한 후기를 확인하세요</p>
|
||||
</div>
|
||||
|
||||
<!-- 검색 영역 -->
|
||||
<div class="search-area">
|
||||
<div class="search-box">
|
||||
<input type="text" id="searchTitle" placeholder="제목으로 검색" />
|
||||
<button id="btnSearch" class="search-btn">검색</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 시술후기 카드 리스트 -->
|
||||
<div class="review-grid" id="review-grid">
|
||||
<div class="loading">고객후기를 불러오는 중...</div>
|
||||
</div>
|
||||
|
||||
<!-- 페이지네이션 -->
|
||||
<div class="pagination-area" id="pagination-area"></div>
|
||||
</div>
|
||||
</th:block>
|
||||
|
||||
<th:block layout:fragment="layoutContentScript">
|
||||
<script th:src="@{/js/web/webreview/procedureReviewSelectList.js}"></script>
|
||||
</th:block>
|
||||
|
||||
</html>
|
||||