1.로그 경로 수정
2.팝업 관리 상세 수정 3.팝업뷰 버튼 추가
This commit is contained in:
@@ -5,3 +5,5 @@ target/
|
|||||||
.idea/
|
.idea/
|
||||||
*.iml
|
*.iml
|
||||||
node_modules/
|
node_modules/
|
||||||
|
logs/
|
||||||
|
*.log
|
||||||
|
|||||||
@@ -31,12 +31,15 @@
|
|||||||
- 삭제: `/mfuc/popup/delete.do`
|
- 삭제: `/mfuc/popup/delete.do`
|
||||||
- 순서 변경: `/mfuc/popup/move.do`
|
- 순서 변경: `/mfuc/popup/move.do`
|
||||||
- 목록 화면에 작성자, 작성일시, 노출여부를 표시했습니다.
|
- 목록 화면에 작성자, 작성일시, 노출여부를 표시했습니다.
|
||||||
|
- 게시기간을 선택 입력할 수 있으며, 미입력 시 기간 제한 없이 노출됩니다.
|
||||||
- 등록 시 로그인한 관리자 식별번호를 작성자로 저장합니다.
|
- 등록 시 로그인한 관리자 식별번호를 작성자로 저장합니다.
|
||||||
- 수정 시 기존 작성자 정보는 유지합니다.
|
- 수정 시 기존 작성자 정보는 유지합니다.
|
||||||
|
|
||||||
## 사용자 메인 팝업
|
## 사용자 메인 팝업
|
||||||
|
|
||||||
- 메인 페이지 진입 시 `USE_YN = 'Y'`, `DEL_YN = 'N'` 팝업만 노출합니다.
|
- 메인 페이지 진입 시 `USE_YN = 'Y'`, `DEL_YN = 'N'` 팝업만 노출합니다.
|
||||||
|
- 게시 시작일시와 종료일시가 설정된 경우 현재 일시가 게시기간 안에 있는 팝업만 노출합니다.
|
||||||
|
- 게시기간이 비어 있는 팝업은 기간 제한 없이 계속 노출합니다.
|
||||||
- 여러 팝업이 있으면 5초 간격으로 자동 이동합니다.
|
- 여러 팝업이 있으면 5초 간격으로 자동 이동합니다.
|
||||||
- 마지막 팝업 이후에는 첫 번째 팝업으로 돌아갑니다.
|
- 마지막 팝업 이후에는 첫 번째 팝업으로 돌아갑니다.
|
||||||
- 좌우 이동 버튼과 하단 불릿 이동을 지원합니다.
|
- 좌우 이동 버튼과 하단 불릿 이동을 지원합니다.
|
||||||
|
|||||||
-2131
File diff suppressed because it is too large
Load Diff
-2117
File diff suppressed because it is too large
Load Diff
-53
@@ -1,53 +0,0 @@
|
|||||||
|
|
||||||
D:\Projects\998.IOTDOOR\iotdoor_v5>if "Windows_NT" == "Windows_NT" setlocal
|
|
||||||
|
|
||||||
D:\Projects\998.IOTDOOR\iotdoor_v5>set DIRNAME=D:\Projects\998.IOTDOOR\iotdoor_v5\
|
|
||||||
|
|
||||||
D:\Projects\998.IOTDOOR\iotdoor_v5>if "D:\Projects\998.IOTDOOR\iotdoor_v5\" == "" set DIRNAME=.
|
|
||||||
|
|
||||||
D:\Projects\998.IOTDOOR\iotdoor_v5>set APP_BASE_NAME=gradlew
|
|
||||||
|
|
||||||
D:\Projects\998.IOTDOOR\iotdoor_v5>set APP_HOME=D:\Projects\998.IOTDOOR\iotdoor_v5\
|
|
||||||
|
|
||||||
D:\Projects\998.IOTDOOR\iotdoor_v5>for %i in ("D:\Projects\998.IOTDOOR\iotdoor_v5\") do set APP_HOME=%~fi
|
|
||||||
|
|
||||||
D:\Projects\998.IOTDOOR\iotdoor_v5>set APP_HOME=D:\Projects\998.IOTDOOR\iotdoor_v5\
|
|
||||||
|
|
||||||
D:\Projects\998.IOTDOOR\iotdoor_v5>set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
|
||||||
|
|
||||||
D:\Projects\998.IOTDOOR\iotdoor_v5>if defined JAVA_HOME goto findJavaFromJavaHome
|
|
||||||
|
|
||||||
D:\Projects\998.IOTDOOR\iotdoor_v5>set JAVA_HOME=C:\Program Files\Java\jdk-17
|
|
||||||
|
|
||||||
D:\Projects\998.IOTDOOR\iotdoor_v5>set JAVA_EXE=C:\Program Files\Java\jdk-17 /bin/java.exe
|
|
||||||
|
|
||||||
D:\Projects\998.IOTDOOR\iotdoor_v5>if exist "C:\Program Files\Java\jdk-17 /bin/java.exe" goto execute
|
|
||||||
|
|
||||||
D:\Projects\998.IOTDOOR\iotdoor_v5>echo. 1>&2
|
|
||||||
|
|
||||||
|
|
||||||
D:\Projects\998.IOTDOOR\iotdoor_v5>echo ERROR: JAVA_HOME is set to an invalid directory: C:\Program Files\Java\jdk-17 1>&2
|
|
||||||
ERROR: JAVA_HOME is set to an invalid directory: C:\Program Files\Java\jdk-17
|
|
||||||
|
|
||||||
D:\Projects\998.IOTDOOR\iotdoor_v5>echo. 1>&2
|
|
||||||
|
|
||||||
|
|
||||||
D:\Projects\998.IOTDOOR\iotdoor_v5>echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
|
||||||
Please set the JAVA_HOME variable in your environment to match the
|
|
||||||
|
|
||||||
D:\Projects\998.IOTDOOR\iotdoor_v5>echo location of your Java installation. 1>&2
|
|
||||||
location of your Java installation.
|
|
||||||
|
|
||||||
D:\Projects\998.IOTDOOR\iotdoor_v5>goto fail
|
|
||||||
|
|
||||||
D:\Projects\998.IOTDOOR\iotdoor_v5>rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
|
||||||
|
|
||||||
D:\Projects\998.IOTDOOR\iotdoor_v5>rem the _cmd.exe /c_ return code!
|
|
||||||
|
|
||||||
D:\Projects\998.IOTDOOR\iotdoor_v5>set EXIT_CODE=0
|
|
||||||
|
|
||||||
D:\Projects\998.IOTDOOR\iotdoor_v5>if 0 EQU 0 set EXIT_CODE=1
|
|
||||||
|
|
||||||
D:\Projects\998.IOTDOOR\iotdoor_v5>if not "" == "" exit 1
|
|
||||||
|
|
||||||
D:\Projects\998.IOTDOOR\iotdoor_v5>exit /b 1
|
|
||||||
@@ -19,6 +19,8 @@ public interface MainPopupMapper {
|
|||||||
p.TTL AS TITLE,
|
p.TTL AS TITLE,
|
||||||
p.CN AS CONTENT,
|
p.CN AS CONTENT,
|
||||||
p.SORT_ORDR AS SORT_ORDER,
|
p.SORT_ORDR AS SORT_ORDER,
|
||||||
|
p.PSTG_BGNG_DT AS PUBLISH_START_AT,
|
||||||
|
p.PSTG_END_DT AS PUBLISH_END_AT,
|
||||||
p.USE_YN,
|
p.USE_YN,
|
||||||
p.DEL_YN,
|
p.DEL_YN,
|
||||||
p.REG_DT AS REGISTERED_AT,
|
p.REG_DT AS REGISTERED_AT,
|
||||||
@@ -39,6 +41,8 @@ public interface MainPopupMapper {
|
|||||||
p.TTL AS TITLE,
|
p.TTL AS TITLE,
|
||||||
p.CN AS CONTENT,
|
p.CN AS CONTENT,
|
||||||
p.SORT_ORDR AS SORT_ORDER,
|
p.SORT_ORDR AS SORT_ORDER,
|
||||||
|
p.PSTG_BGNG_DT AS PUBLISH_START_AT,
|
||||||
|
p.PSTG_END_DT AS PUBLISH_END_AT,
|
||||||
p.USE_YN,
|
p.USE_YN,
|
||||||
p.DEL_YN,
|
p.DEL_YN,
|
||||||
p.REG_DT AS REGISTERED_AT,
|
p.REG_DT AS REGISTERED_AT,
|
||||||
@@ -49,6 +53,8 @@ public interface MainPopupMapper {
|
|||||||
JOIN TN_USER u ON u.USER_IDNTF_NO = p.REG_USER_IDNTF_NO
|
JOIN TN_USER u ON u.USER_IDNTF_NO = p.REG_USER_IDNTF_NO
|
||||||
WHERE p.DEL_YN = 'N'
|
WHERE p.DEL_YN = 'N'
|
||||||
AND p.USE_YN = 'Y'
|
AND p.USE_YN = 'Y'
|
||||||
|
AND (p.PSTG_BGNG_DT IS NULL OR p.PSTG_BGNG_DT <= CURRENT_TIMESTAMP)
|
||||||
|
AND (p.PSTG_END_DT IS NULL OR p.PSTG_END_DT >= CURRENT_TIMESTAMP)
|
||||||
ORDER BY p.SORT_ORDR ASC, p.REG_DT ASC
|
ORDER BY p.SORT_ORDR ASC, p.REG_DT ASC
|
||||||
""")
|
""")
|
||||||
List<MainPopup> selectPublicPopups();
|
List<MainPopup> selectPublicPopups();
|
||||||
@@ -60,6 +66,8 @@ public interface MainPopupMapper {
|
|||||||
p.TTL AS TITLE,
|
p.TTL AS TITLE,
|
||||||
p.CN AS CONTENT,
|
p.CN AS CONTENT,
|
||||||
p.SORT_ORDR AS SORT_ORDER,
|
p.SORT_ORDR AS SORT_ORDER,
|
||||||
|
p.PSTG_BGNG_DT AS PUBLISH_START_AT,
|
||||||
|
p.PSTG_END_DT AS PUBLISH_END_AT,
|
||||||
p.USE_YN,
|
p.USE_YN,
|
||||||
p.DEL_YN,
|
p.DEL_YN,
|
||||||
p.REG_DT AS REGISTERED_AT,
|
p.REG_DT AS REGISTERED_AT,
|
||||||
@@ -80,6 +88,8 @@ public interface MainPopupMapper {
|
|||||||
p.TTL AS TITLE,
|
p.TTL AS TITLE,
|
||||||
p.CN AS CONTENT,
|
p.CN AS CONTENT,
|
||||||
p.SORT_ORDR AS SORT_ORDER,
|
p.SORT_ORDR AS SORT_ORDER,
|
||||||
|
p.PSTG_BGNG_DT AS PUBLISH_START_AT,
|
||||||
|
p.PSTG_END_DT AS PUBLISH_END_AT,
|
||||||
p.USE_YN,
|
p.USE_YN,
|
||||||
p.DEL_YN,
|
p.DEL_YN,
|
||||||
p.REG_DT AS REGISTERED_AT,
|
p.REG_DT AS REGISTERED_AT,
|
||||||
@@ -102,6 +112,8 @@ public interface MainPopupMapper {
|
|||||||
p.TTL AS TITLE,
|
p.TTL AS TITLE,
|
||||||
p.CN AS CONTENT,
|
p.CN AS CONTENT,
|
||||||
p.SORT_ORDR AS SORT_ORDER,
|
p.SORT_ORDR AS SORT_ORDER,
|
||||||
|
p.PSTG_BGNG_DT AS PUBLISH_START_AT,
|
||||||
|
p.PSTG_END_DT AS PUBLISH_END_AT,
|
||||||
p.USE_YN,
|
p.USE_YN,
|
||||||
p.DEL_YN,
|
p.DEL_YN,
|
||||||
p.REG_DT AS REGISTERED_AT,
|
p.REG_DT AS REGISTERED_AT,
|
||||||
@@ -132,6 +144,8 @@ public interface MainPopupMapper {
|
|||||||
TTL,
|
TTL,
|
||||||
CN,
|
CN,
|
||||||
SORT_ORDR,
|
SORT_ORDR,
|
||||||
|
PSTG_BGNG_DT,
|
||||||
|
PSTG_END_DT,
|
||||||
USE_YN,
|
USE_YN,
|
||||||
DEL_YN,
|
DEL_YN,
|
||||||
REG_DT,
|
REG_DT,
|
||||||
@@ -142,6 +156,8 @@ public interface MainPopupMapper {
|
|||||||
#{title},
|
#{title},
|
||||||
#{content},
|
#{content},
|
||||||
#{sortOrder},
|
#{sortOrder},
|
||||||
|
#{publishStartAt},
|
||||||
|
#{publishEndAt},
|
||||||
#{useYn},
|
#{useYn},
|
||||||
'N',
|
'N',
|
||||||
CURRENT_TIMESTAMP,
|
CURRENT_TIMESTAMP,
|
||||||
@@ -157,6 +173,8 @@ public interface MainPopupMapper {
|
|||||||
SET
|
SET
|
||||||
TTL = #{title},
|
TTL = #{title},
|
||||||
CN = #{content},
|
CN = #{content},
|
||||||
|
PSTG_BGNG_DT = #{publishStartAt},
|
||||||
|
PSTG_END_DT = #{publishEndAt},
|
||||||
USE_YN = #{useYn},
|
USE_YN = #{useYn},
|
||||||
UPD_DT = CURRENT_TIMESTAMP
|
UPD_DT = CURRENT_TIMESTAMP
|
||||||
WHERE POPUP_IDNTF_NO = #{popupIdntfNo}
|
WHERE POPUP_IDNTF_NO = #{popupIdntfNo}
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ public class MainPopup {
|
|||||||
private String title;
|
private String title;
|
||||||
private String content;
|
private String content;
|
||||||
private int sortOrder;
|
private int sortOrder;
|
||||||
|
private LocalDateTime publishStartAt;
|
||||||
|
private LocalDateTime publishEndAt;
|
||||||
private String useYn;
|
private String useYn;
|
||||||
private String delYn;
|
private String delYn;
|
||||||
private LocalDateTime registeredAt;
|
private LocalDateTime registeredAt;
|
||||||
@@ -55,6 +57,26 @@ public class MainPopup {
|
|||||||
this.sortOrder = sortOrder;
|
this.sortOrder = sortOrder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 게시 시작 일시를 반환합니다. */
|
||||||
|
public LocalDateTime getPublishStartAt() {
|
||||||
|
return publishStartAt;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 게시 시작 일시를 설정합니다. */
|
||||||
|
public void setPublishStartAt(LocalDateTime publishStartAt) {
|
||||||
|
this.publishStartAt = publishStartAt;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 게시 종료 일시를 반환합니다. */
|
||||||
|
public LocalDateTime getPublishEndAt() {
|
||||||
|
return publishEndAt;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 게시 종료 일시를 설정합니다. */
|
||||||
|
public void setPublishEndAt(LocalDateTime publishEndAt) {
|
||||||
|
this.publishEndAt = publishEndAt;
|
||||||
|
}
|
||||||
|
|
||||||
/** 노출 여부를 반환합니다. */
|
/** 노출 여부를 반환합니다. */
|
||||||
public String getUseYn() {
|
public String getUseYn() {
|
||||||
return useYn;
|
return useYn;
|
||||||
|
|||||||
@@ -2,6 +2,9 @@ package kr.iotdoor.boot.model.popup;
|
|||||||
|
|
||||||
import jakarta.validation.constraints.NotBlank;
|
import jakarta.validation.constraints.NotBlank;
|
||||||
import jakarta.validation.constraints.Size;
|
import jakarta.validation.constraints.Size;
|
||||||
|
import org.springframework.format.annotation.DateTimeFormat;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
public class MainPopupForm {
|
public class MainPopupForm {
|
||||||
|
|
||||||
@@ -14,6 +17,12 @@ public class MainPopupForm {
|
|||||||
@NotBlank(message = "내용을 입력해주세요.")
|
@NotBlank(message = "내용을 입력해주세요.")
|
||||||
private String content;
|
private String content;
|
||||||
|
|
||||||
|
@DateTimeFormat(pattern = "yyyy-MM-dd'T'HH:mm")
|
||||||
|
private LocalDateTime publishStartAt;
|
||||||
|
|
||||||
|
@DateTimeFormat(pattern = "yyyy-MM-dd'T'HH:mm")
|
||||||
|
private LocalDateTime publishEndAt;
|
||||||
|
|
||||||
private String useYn = "Y";
|
private String useYn = "Y";
|
||||||
|
|
||||||
/** 팝업 식별번호를 반환합니다. */
|
/** 팝업 식별번호를 반환합니다. */
|
||||||
@@ -46,6 +55,26 @@ public class MainPopupForm {
|
|||||||
this.content = content;
|
this.content = content;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 게시 시작 일시를 반환합니다. */
|
||||||
|
public LocalDateTime getPublishStartAt() {
|
||||||
|
return publishStartAt;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 게시 시작 일시를 설정합니다. */
|
||||||
|
public void setPublishStartAt(LocalDateTime publishStartAt) {
|
||||||
|
this.publishStartAt = publishStartAt;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 게시 종료 일시를 반환합니다. */
|
||||||
|
public LocalDateTime getPublishEndAt() {
|
||||||
|
return publishEndAt;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 게시 종료 일시를 설정합니다. */
|
||||||
|
public void setPublishEndAt(LocalDateTime publishEndAt) {
|
||||||
|
this.publishEndAt = publishEndAt;
|
||||||
|
}
|
||||||
|
|
||||||
/** 노출 여부를 반환합니다. */
|
/** 노출 여부를 반환합니다. */
|
||||||
public String getUseYn() {
|
public String getUseYn() {
|
||||||
return useYn;
|
return useYn;
|
||||||
|
|||||||
@@ -50,6 +50,8 @@ public class MainPopupService {
|
|||||||
form.setPopupIdntfNo(popup.getPopupIdntfNo());
|
form.setPopupIdntfNo(popup.getPopupIdntfNo());
|
||||||
form.setTitle(popup.getTitle());
|
form.setTitle(popup.getTitle());
|
||||||
form.setContent(popup.getContent());
|
form.setContent(popup.getContent());
|
||||||
|
form.setPublishStartAt(popup.getPublishStartAt());
|
||||||
|
form.setPublishEndAt(popup.getPublishEndAt());
|
||||||
form.setUseYn("Y".equals(popup.getUseYn()) ? "Y" : "N");
|
form.setUseYn("Y".equals(popup.getUseYn()) ? "Y" : "N");
|
||||||
return form;
|
return form;
|
||||||
}
|
}
|
||||||
@@ -110,6 +112,8 @@ public class MainPopupService {
|
|||||||
MainPopup popup = new MainPopup();
|
MainPopup popup = new MainPopup();
|
||||||
popup.setTitle(form.getTitle().trim());
|
popup.setTitle(form.getTitle().trim());
|
||||||
popup.setContent(form.getContent().trim());
|
popup.setContent(form.getContent().trim());
|
||||||
|
popup.setPublishStartAt(form.getPublishStartAt());
|
||||||
|
popup.setPublishEndAt(form.getPublishEndAt());
|
||||||
popup.setUseYn("Y".equals(form.getUseYn()) ? "Y" : "N");
|
popup.setUseYn("Y".equals(form.getUseYn()) ? "Y" : "N");
|
||||||
return popup;
|
return popup;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ public class AdminMainPopupController {
|
|||||||
@GetMapping("/mfuc/popup/list.do")
|
@GetMapping("/mfuc/popup/list.do")
|
||||||
public String popupList(Model model) {
|
public String popupList(Model model) {
|
||||||
model.addAttribute("popups", mainPopupService.getAdminPopups());
|
model.addAttribute("popups", mainPopupService.getAdminPopups());
|
||||||
|
model.addAttribute("publicPopups", mainPopupService.getPublicPopups());
|
||||||
model.addAttribute("adminMenu", "popup");
|
model.addAttribute("adminMenu", "popup");
|
||||||
model.addAttribute("pageTitle", "팝업 관리");
|
model.addAttribute("pageTitle", "팝업 관리");
|
||||||
model.addAttribute("pageDescription", "메인 진입 시 노출할 팝업의 내용, 노출 여부, 순서를 관리합니다.");
|
model.addAttribute("pageDescription", "메인 진입 시 노출할 팝업의 내용, 노출 여부, 순서를 관리합니다.");
|
||||||
@@ -56,6 +57,10 @@ public class AdminMainPopupController {
|
|||||||
) {
|
) {
|
||||||
form.setUseYn("Y".equals(useYn) ? "Y" : "N");
|
form.setUseYn("Y".equals(useYn) ? "Y" : "N");
|
||||||
|
|
||||||
|
if (hasInvalidPublishPeriod(form)) {
|
||||||
|
bindingResult.rejectValue("publishEndAt", "period", "게시 종료일시는 시작일시 이후로 입력해주세요.");
|
||||||
|
}
|
||||||
|
|
||||||
if (bindingResult.hasErrors()) {
|
if (bindingResult.hasErrors()) {
|
||||||
applyFormPage(model, form);
|
applyFormPage(model, form);
|
||||||
return "kr/iotdoor/boot/admin/popup/form";
|
return "kr/iotdoor/boot/admin/popup/form";
|
||||||
@@ -94,4 +99,11 @@ public class AdminMainPopupController {
|
|||||||
model.addAttribute("pageTitle", StringUtils.hasText(form.getPopupIdntfNo()) ? "팝업 수정" : "팝업 등록");
|
model.addAttribute("pageTitle", StringUtils.hasText(form.getPopupIdntfNo()) ? "팝업 수정" : "팝업 등록");
|
||||||
model.addAttribute("pageDescription", "메인 진입 시 모달로 노출할 팝업 내용을 작성합니다.");
|
model.addAttribute("pageDescription", "메인 진입 시 모달로 노출할 팝업 내용을 작성합니다.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 게시 시작 일시가 종료 일시보다 늦은지 확인합니다. */
|
||||||
|
private boolean hasInvalidPublishPeriod(MainPopupForm form) {
|
||||||
|
return form.getPublishStartAt() != null
|
||||||
|
&& form.getPublishEndAt() != null
|
||||||
|
&& form.getPublishStartAt().isAfter(form.getPublishEndAt());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -102,6 +102,8 @@ CREATE TABLE IF NOT EXISTS TN_MAIN_POPUP (
|
|||||||
TTL VARCHAR(200) NOT NULL,
|
TTL VARCHAR(200) NOT NULL,
|
||||||
CN TEXT NOT NULL,
|
CN TEXT NOT NULL,
|
||||||
SORT_ORDR INT NOT NULL DEFAULT 0,
|
SORT_ORDR INT NOT NULL DEFAULT 0,
|
||||||
|
PSTG_BGNG_DT DATETIME NULL,
|
||||||
|
PSTG_END_DT DATETIME NULL,
|
||||||
USE_YN CHAR(1) NOT NULL DEFAULT 'Y',
|
USE_YN CHAR(1) NOT NULL DEFAULT 'Y',
|
||||||
DEL_YN CHAR(1) NOT NULL DEFAULT 'N',
|
DEL_YN CHAR(1) NOT NULL DEFAULT 'N',
|
||||||
REG_DT DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
REG_DT DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
@@ -112,6 +114,9 @@ CREATE TABLE IF NOT EXISTS TN_MAIN_POPUP (
|
|||||||
REFERENCES TN_USER (USER_IDNTF_NO)
|
REFERENCES TN_USER (USER_IDNTF_NO)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
ALTER TABLE TN_MAIN_POPUP ADD COLUMN IF NOT EXISTS PSTG_BGNG_DT DATETIME NULL;
|
||||||
|
ALTER TABLE TN_MAIN_POPUP ADD COLUMN IF NOT EXISTS PSTG_END_DT DATETIME NULL;
|
||||||
|
|
||||||
CREATE INDEX IF NOT EXISTS IDX_TN_ATCH_FILE_DTL__PATH ON TN_ATCH_FILE_DTL (FILE_STRG_PATH_NM);
|
CREATE INDEX IF NOT EXISTS IDX_TN_ATCH_FILE_DTL__PATH ON TN_ATCH_FILE_DTL (FILE_STRG_PATH_NM);
|
||||||
CREATE INDEX IF NOT EXISTS IDX_TN_PORTFOLIO__REG_DT ON TN_PORTFOLIO (REG_DT);
|
CREATE INDEX IF NOT EXISTS IDX_TN_PORTFOLIO__REG_DT ON TN_PORTFOLIO (REG_DT);
|
||||||
CREATE INDEX IF NOT EXISTS IDX_TN_CONTACT_INQRY__SUBMIT_DT ON TN_CONTACT_INQRY (SUBMIT_DT);
|
CREATE INDEX IF NOT EXISTS IDX_TN_CONTACT_INQRY__SUBMIT_DT ON TN_CONTACT_INQRY (SUBMIT_DT);
|
||||||
@@ -123,6 +128,7 @@ CREATE INDEX IF NOT EXISTS IDX_TN_MAIN_BANNER__SORT ON TN_MAIN_BANNER (SORT_ORDR
|
|||||||
CREATE INDEX IF NOT EXISTS IDX_TN_MAIN_BANNER__USE_DEL ON TN_MAIN_BANNER (USE_YN, DEL_YN);
|
CREATE INDEX IF NOT EXISTS IDX_TN_MAIN_BANNER__USE_DEL ON TN_MAIN_BANNER (USE_YN, DEL_YN);
|
||||||
CREATE INDEX IF NOT EXISTS IDX_TN_MAIN_POPUP__SORT ON TN_MAIN_POPUP (SORT_ORDR);
|
CREATE INDEX IF NOT EXISTS IDX_TN_MAIN_POPUP__SORT ON TN_MAIN_POPUP (SORT_ORDR);
|
||||||
CREATE INDEX IF NOT EXISTS IDX_TN_MAIN_POPUP__USE_DEL ON TN_MAIN_POPUP (USE_YN, DEL_YN);
|
CREATE INDEX IF NOT EXISTS IDX_TN_MAIN_POPUP__USE_DEL ON TN_MAIN_POPUP (USE_YN, DEL_YN);
|
||||||
|
CREATE INDEX IF NOT EXISTS IDX_TN_MAIN_POPUP__PUBLISH ON TN_MAIN_POPUP (PSTG_BGNG_DT, PSTG_END_DT);
|
||||||
|
|
||||||
INSERT INTO TN_MAIN_BANNER (
|
INSERT INTO TN_MAIN_BANNER (
|
||||||
BANNER_IDNTF_NO, TTL, CN, IMAGE_URL, SORT_ORDR, USE_YN, DEL_YN, REG_DT
|
BANNER_IDNTF_NO, TTL, CN, IMAGE_URL, SORT_ORDR, USE_YN, DEL_YN, REG_DT
|
||||||
|
|||||||
@@ -252,7 +252,7 @@ body {
|
|||||||
|
|
||||||
.iotdoor-popup-table {
|
.iotdoor-popup-table {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
min-width: 1080px;
|
min-width: 1220px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.iotdoor-banner-list-content {
|
.iotdoor-banner-list-content {
|
||||||
@@ -270,6 +270,134 @@ body {
|
|||||||
height: auto;
|
height: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.admin-popup-preview-modal {
|
||||||
|
position: fixed;
|
||||||
|
inset: 0;
|
||||||
|
z-index: 10000;
|
||||||
|
display: none;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 24px;
|
||||||
|
background: rgba(15, 23, 42, 0.55);
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-popup-preview-modal.is-open {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-popup-preview-dialog {
|
||||||
|
width: min(760px, 92vw);
|
||||||
|
max-height: 78vh;
|
||||||
|
overflow: hidden;
|
||||||
|
border-radius: 18px;
|
||||||
|
background: #ffffff;
|
||||||
|
box-shadow: 0 28px 80px rgba(15, 23, 42, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-popup-preview-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 16px;
|
||||||
|
padding: 20px 24px;
|
||||||
|
border-bottom: 1px solid #e5e7eb;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-popup-preview-title {
|
||||||
|
margin: 0;
|
||||||
|
color: #111827;
|
||||||
|
font-size: 22px;
|
||||||
|
font-weight: 700;
|
||||||
|
line-height: 1.35;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-popup-preview-close {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
width: 38px;
|
||||||
|
height: 38px;
|
||||||
|
border: 0;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: #f3f4f6;
|
||||||
|
color: #111827;
|
||||||
|
font-size: 24px;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-popup-preview-body {
|
||||||
|
position: relative;
|
||||||
|
max-height: calc(78vh - 118px);
|
||||||
|
overflow-x: hidden;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-popup-preview-track {
|
||||||
|
display: flex;
|
||||||
|
transition: transform 0.35s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-popup-preview-slide {
|
||||||
|
flex: 0 0 100%;
|
||||||
|
padding: 26px 30px;
|
||||||
|
color: #374151;
|
||||||
|
line-height: 1.75;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-popup-preview-slide img {
|
||||||
|
max-width: 100%;
|
||||||
|
height: auto;
|
||||||
|
border-radius: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-popup-preview-nav {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
width: 38px;
|
||||||
|
height: 38px;
|
||||||
|
border: 0;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: rgba(17, 24, 39, 0.72);
|
||||||
|
color: #ffffff;
|
||||||
|
font-size: 22px;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-popup-preview-nav-prev {
|
||||||
|
left: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-popup-preview-nav-next {
|
||||||
|
right: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-popup-preview-bullets {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 8px;
|
||||||
|
padding: 0 24px 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-popup-preview-bullet {
|
||||||
|
width: 10px;
|
||||||
|
height: 10px;
|
||||||
|
padding: 0;
|
||||||
|
border: 0;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: #cbd5e1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-popup-preview-bullet.is-active {
|
||||||
|
background: #1f5aa0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-popup-preview-empty {
|
||||||
|
padding: 34px 24px;
|
||||||
|
color: #667085;
|
||||||
|
font-size: 0.95rem;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
.iotdoor-banner-form-grid {
|
.iotdoor-banner-form-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: 1.5rem;
|
gap: 1.5rem;
|
||||||
@@ -332,3 +460,40 @@ body {
|
|||||||
grid-template-columns: 360px 1fr;
|
grid-template-columns: 360px 1fr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (max-width: 575.98px) {
|
||||||
|
.admin-popup-preview-modal {
|
||||||
|
align-items: flex-start;
|
||||||
|
padding: 16px 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-popup-preview-dialog {
|
||||||
|
width: 100%;
|
||||||
|
max-height: 84vh;
|
||||||
|
border-radius: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-popup-preview-header {
|
||||||
|
padding: 16px 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-popup-preview-title {
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-popup-preview-body {
|
||||||
|
max-height: calc(84vh - 104px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-popup-preview-slide {
|
||||||
|
padding: 22px 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-popup-preview-nav {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-popup-preview-bullets {
|
||||||
|
padding: 0 18px 14px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -85,6 +85,26 @@
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label class="mb-2 block text-sm font-medium text-gray-700">게시기간</label>
|
||||||
|
<div class="grid gap-4 md:grid-cols-2">
|
||||||
|
<div>
|
||||||
|
<input class="h-11 w-full rounded-lg border border-gray-300 bg-white px-4 text-sm text-gray-800 outline-none transition focus:border-brand-300 focus:ring-3 focus:ring-brand-500/10"
|
||||||
|
type="datetime-local"
|
||||||
|
th:field="*{publishStartAt}">
|
||||||
|
<p class="mt-2 text-sm text-gray-500">미입력 시 시작 제한 없이 노출됩니다.</p>
|
||||||
|
<div class="mt-2 text-sm text-error-600" th:if="${#fields.hasErrors('publishStartAt')}" th:errors="*{publishStartAt}">게시 시작 오류</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<input class="h-11 w-full rounded-lg border border-gray-300 bg-white px-4 text-sm text-gray-800 outline-none transition focus:border-brand-300 focus:ring-3 focus:ring-brand-500/10"
|
||||||
|
type="datetime-local"
|
||||||
|
th:field="*{publishEndAt}">
|
||||||
|
<p class="mt-2 text-sm text-gray-500">미입력 시 종료 제한 없이 계속 노출됩니다.</p>
|
||||||
|
<div class="mt-2 text-sm text-error-600" th:if="${#fields.hasErrors('publishEndAt')}" th:errors="*{publishEndAt}">게시 종료 오류</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="flex flex-wrap gap-3">
|
<div class="flex flex-wrap gap-3">
|
||||||
<button class="inline-flex items-center gap-2 rounded-lg bg-brand-500 px-4 py-2 text-sm font-medium text-white hover:bg-brand-600"
|
<button class="inline-flex items-center gap-2 rounded-lg bg-brand-500 px-4 py-2 text-sm font-medium text-white hover:bg-brand-600"
|
||||||
type="submit">
|
type="submit">
|
||||||
|
|||||||
@@ -10,11 +10,18 @@
|
|||||||
<h2 class="text-xl font-semibold text-gray-800">팝업관리</h2>
|
<h2 class="text-xl font-semibold text-gray-800">팝업관리</h2>
|
||||||
<p class="mt-1 text-sm text-gray-500">메인 진입 팝업의 노출 순서와 내용을 관리합니다.</p>
|
<p class="mt-1 text-sm text-gray-500">메인 진입 팝업의 노출 순서와 내용을 관리합니다.</p>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="flex flex-wrap gap-2">
|
||||||
|
<button id="adminPopupPreviewOpen"
|
||||||
|
class="inline-flex items-center gap-2 rounded-lg border border-brand-200 bg-white px-4 py-2 text-sm font-medium text-brand-600 hover:bg-brand-25"
|
||||||
|
type="button">
|
||||||
|
팝업 목록 확인
|
||||||
|
</button>
|
||||||
<a class="inline-flex items-center gap-2 rounded-lg bg-brand-500 px-4 py-2 text-sm font-medium text-white hover:bg-brand-600"
|
<a class="inline-flex items-center gap-2 rounded-lg bg-brand-500 px-4 py-2 text-sm font-medium text-white hover:bg-brand-600"
|
||||||
th:href="@{/mfuc/popup/form.do}">
|
th:href="@{/mfuc/popup/form.do}">
|
||||||
새 팝업 등록
|
새 팝업 등록
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="rounded-xl border border-success-100 bg-success-50 px-4 py-3 text-sm text-success-700" th:if="${param.saved}">
|
<div class="rounded-xl border border-success-100 bg-success-50 px-4 py-3 text-sm text-success-700" th:if="${param.saved}">
|
||||||
팝업이 저장되었습니다.
|
팝업이 저장되었습니다.
|
||||||
@@ -41,6 +48,7 @@
|
|||||||
<tr class="border-b border-gray-100 text-left text-gray-500">
|
<tr class="border-b border-gray-100 text-left text-gray-500">
|
||||||
<th class="px-3 py-3">순서</th>
|
<th class="px-3 py-3">순서</th>
|
||||||
<th class="px-3 py-3">제목/내용</th>
|
<th class="px-3 py-3">제목/내용</th>
|
||||||
|
<th class="px-3 py-3">게시기간</th>
|
||||||
<th class="px-3 py-3">작성자</th>
|
<th class="px-3 py-3">작성자</th>
|
||||||
<th class="px-3 py-3">작성일시</th>
|
<th class="px-3 py-3">작성일시</th>
|
||||||
<th class="px-3 py-3">노출</th>
|
<th class="px-3 py-3">노출</th>
|
||||||
@@ -78,6 +86,14 @@
|
|||||||
<div class="font-semibold text-gray-900" th:text="${popup.title}">팝업 제목</div>
|
<div class="font-semibold text-gray-900" th:text="${popup.title}">팝업 제목</div>
|
||||||
<div class="iotdoor-popup-list-content mt-2 text-sm leading-6 text-gray-500" th:utext="${popup.content}">팝업 내용</div>
|
<div class="iotdoor-popup-list-content mt-2 text-sm leading-6 text-gray-500" th:utext="${popup.content}">팝업 내용</div>
|
||||||
</td>
|
</td>
|
||||||
|
<td class="whitespace-nowrap px-3 py-4 text-gray-500">
|
||||||
|
<div th:if="${popup.publishStartAt == null and popup.publishEndAt == null}">기간 제한 없음</div>
|
||||||
|
<div th:unless="${popup.publishStartAt == null and popup.publishEndAt == null}">
|
||||||
|
<span th:text="${popup.publishStartAt != null ? #temporals.format(popup.publishStartAt, 'yyyy-MM-dd HH:mm') : '시작 제한 없음'}">시작</span>
|
||||||
|
<span class="mx-1">~</span>
|
||||||
|
<span th:text="${popup.publishEndAt != null ? #temporals.format(popup.publishEndAt, 'yyyy-MM-dd HH:mm') : '종료 제한 없음'}">종료</span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
<td class="whitespace-nowrap px-3 py-4 text-gray-500" th:text="${popup.registeredByName}">관리자</td>
|
<td class="whitespace-nowrap px-3 py-4 text-gray-500" th:text="${popup.registeredByName}">관리자</td>
|
||||||
<td class="whitespace-nowrap px-3 py-4 text-gray-500"
|
<td class="whitespace-nowrap px-3 py-4 text-gray-500"
|
||||||
th:text="${#temporals.format(popup.registeredAt, 'yyyy-MM-dd HH:mm')}">2026-04-21 09:00</td>
|
th:text="${#temporals.format(popup.registeredAt, 'yyyy-MM-dd HH:mm')}">2026-04-21 09:00</td>
|
||||||
@@ -109,6 +125,154 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<div id="adminPopupPreviewModal"
|
||||||
|
class="admin-popup-preview-modal"
|
||||||
|
role="dialog"
|
||||||
|
aria-modal="true"
|
||||||
|
aria-labelledby="adminPopupPreviewTitle">
|
||||||
|
<div class="admin-popup-preview-dialog">
|
||||||
|
<div class="admin-popup-preview-header">
|
||||||
|
<h2 id="adminPopupPreviewTitle" class="admin-popup-preview-title">알림</h2>
|
||||||
|
<button id="adminPopupPreviewClose" class="admin-popup-preview-close" type="button" aria-label="팝업 닫기">×</button>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="admin-popup-preview-empty" th:if="${#lists.isEmpty(publicPopups)}">
|
||||||
|
현재 사용자에게 노출되는 팝업이 없습니다.
|
||||||
|
</div>
|
||||||
|
<th:block th:unless="${#lists.isEmpty(publicPopups)}">
|
||||||
|
<div class="admin-popup-preview-body">
|
||||||
|
<div id="adminPopupPreviewTrack" class="admin-popup-preview-track">
|
||||||
|
<article class="admin-popup-preview-slide"
|
||||||
|
th:each="popup : ${publicPopups}"
|
||||||
|
th:attr="data-title=${popup.title}">
|
||||||
|
<div th:utext="${popup.content}">팝업 내용</div>
|
||||||
|
</article>
|
||||||
|
</div>
|
||||||
|
<button id="adminPopupPreviewPrev" class="admin-popup-preview-nav admin-popup-preview-nav-prev" type="button" aria-label="이전 팝업">‹</button>
|
||||||
|
<button id="adminPopupPreviewNext" class="admin-popup-preview-nav admin-popup-preview-nav-next" type="button" aria-label="다음 팝업">›</button>
|
||||||
|
</div>
|
||||||
|
<div id="adminPopupPreviewBullets" class="admin-popup-preview-bullets" aria-label="팝업 목록"></div>
|
||||||
|
</th:block>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<th:block layout:fragment="pageScripts">
|
||||||
|
<script>
|
||||||
|
document.addEventListener('DOMContentLoaded', function () {
|
||||||
|
const openButton = document.getElementById('adminPopupPreviewOpen');
|
||||||
|
const modal = document.getElementById('adminPopupPreviewModal');
|
||||||
|
const closeButton = document.getElementById('adminPopupPreviewClose');
|
||||||
|
const track = document.getElementById('adminPopupPreviewTrack');
|
||||||
|
const title = document.getElementById('adminPopupPreviewTitle');
|
||||||
|
const prevButton = document.getElementById('adminPopupPreviewPrev');
|
||||||
|
const nextButton = document.getElementById('adminPopupPreviewNext');
|
||||||
|
const bulletContainer = document.getElementById('adminPopupPreviewBullets');
|
||||||
|
|
||||||
|
if (!openButton || !modal || !closeButton) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let activeIndex = 0;
|
||||||
|
let timerId = null;
|
||||||
|
const intervalMs = 5000;
|
||||||
|
const slides = track ? Array.from(track.querySelectorAll('.admin-popup-preview-slide')) : [];
|
||||||
|
const bullets = [];
|
||||||
|
|
||||||
|
// 지정한 순번의 팝업으로 미리보기 캐러셀을 이동합니다.
|
||||||
|
function goToSlide(index) {
|
||||||
|
if (!slides.length || !track || !title) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
activeIndex = (index + slides.length) % slides.length;
|
||||||
|
track.style.transform = 'translateX(-' + (activeIndex * 100) + '%)';
|
||||||
|
title.textContent = slides[activeIndex].dataset.title || '알림';
|
||||||
|
bullets.forEach(function (bullet, bulletIndex) {
|
||||||
|
bullet.classList.toggle('is-active', bulletIndex === activeIndex);
|
||||||
|
bullet.setAttribute('aria-current', bulletIndex === activeIndex ? 'true' : 'false');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 자동 롤링 타이머를 재시작합니다.
|
||||||
|
function restartTimer() {
|
||||||
|
if (timerId) {
|
||||||
|
window.clearInterval(timerId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (slides.length <= 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
timerId = window.setInterval(function () {
|
||||||
|
goToSlide(activeIndex + 1);
|
||||||
|
}, intervalMs);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 사용자가 선택한 팝업으로 이동하고 자동 롤링 기준 시간을 초기화합니다.
|
||||||
|
function moveTo(index) {
|
||||||
|
goToSlide(index);
|
||||||
|
restartTimer();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 미리보기 하단 불릿 버튼을 생성합니다.
|
||||||
|
function createBullets() {
|
||||||
|
if (!bulletContainer) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
slides.forEach(function (slide, index) {
|
||||||
|
const bullet = document.createElement('button');
|
||||||
|
bullet.type = 'button';
|
||||||
|
bullet.className = 'admin-popup-preview-bullet';
|
||||||
|
bullet.setAttribute('aria-label', (index + 1) + '번 팝업 보기');
|
||||||
|
bullet.addEventListener('click', function () {
|
||||||
|
moveTo(index);
|
||||||
|
});
|
||||||
|
bulletContainer.appendChild(bullet);
|
||||||
|
bullets.push(bullet);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 미리보기 모달을 엽니다.
|
||||||
|
function openPreview() {
|
||||||
|
modal.classList.add('is-open');
|
||||||
|
goToSlide(activeIndex);
|
||||||
|
restartTimer();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 미리보기 모달을 닫습니다.
|
||||||
|
function closePreview() {
|
||||||
|
modal.classList.remove('is-open');
|
||||||
|
if (timerId) {
|
||||||
|
window.clearInterval(timerId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
createBullets();
|
||||||
|
goToSlide(0);
|
||||||
|
|
||||||
|
openButton.addEventListener('click', openPreview);
|
||||||
|
closeButton.addEventListener('click', closePreview);
|
||||||
|
modal.addEventListener('click', function (event) {
|
||||||
|
if (event.target === modal) {
|
||||||
|
closePreview();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (prevButton) {
|
||||||
|
prevButton.addEventListener('click', function () {
|
||||||
|
moveTo(activeIndex - 1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nextButton) {
|
||||||
|
nextButton.addEventListener('click', function () {
|
||||||
|
moveTo(activeIndex + 1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</th:block>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -63,6 +63,7 @@
|
|||||||
.iotdoor-popup-body {
|
.iotdoor-popup-body {
|
||||||
position: relative;
|
position: relative;
|
||||||
max-height: calc(78vh - 150px);
|
max-height: calc(78vh - 150px);
|
||||||
|
overflow-x: hidden;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 859 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 342 KiB |
Reference in New Issue
Block a user