Ver Fonte

update 2024-02-29

junggilpark há 1 ano atrás
pai
commit
545b643e36
31 ficheiros alterados com 998 adições e 144 exclusões
  1. 14 0
      src/main/java/com/its/web/controller/common/CommonController.java
  2. 3 0
      src/main/java/com/its/web/controller/view/ViewController.java
  3. 2 0
      src/main/java/com/its/web/mapper/its/popup/PopupMapper.java
  4. 77 0
      src/main/java/com/its/web/service/common/CommonService.java
  5. 37 34
      src/main/java/com/its/web/service/popup/PopupService.java
  6. 2 1
      src/main/resources/application-dev.yml
  7. 2 1
      src/main/resources/application-prod.yml
  8. 1 2
      src/main/resources/mybatis/mapper/itcs/InterSectionMapper.xml
  9. 3 2
      src/main/resources/mybatis/mapper/its/notice/NoticeMapper.xml
  10. 19 0
      src/main/resources/mybatis/mapper/its/popup/PopupMapper.xml
  11. 23 2
      src/main/resources/static/css/cctv.css
  12. 3 0
      src/main/resources/static/css/common.css
  13. 87 8
      src/main/resources/static/css/main.css
  14. 42 31
      src/main/resources/static/css/notice.css
  15. 7 0
      src/main/resources/static/css/traffic.css
  16. BIN
      src/main/resources/static/images/background/bg_main2.jpg
  17. BIN
      src/main/resources/static/images/background/bottom_img.jpg
  18. BIN
      src/main/resources/static/images/icon/false.png
  19. BIN
      src/main/resources/static/images/icon/true.png
  20. 42 0
      src/main/resources/static/js/common/common.js
  21. 1 0
      src/main/resources/static/js/naverEditor/SmartEditor2Skin.html
  22. 1 0
      src/main/resources/static/js/naverEditor/js/SE2BasicCreator.js
  23. 10 0
      src/main/resources/static/js/naverEditor/js/SE2M_Configuration.js
  24. 483 0
      src/main/resources/static/js/naverEditor/js/SE_QuickEditor_Image.js
  25. 1 1
      src/main/resources/static/js/naverEditor/sample/photo_uploader/attach_photo.js
  26. 14 12
      src/main/resources/static/js/traffic/traffic.js
  27. 18 8
      src/main/resources/templates/admin/cctv.html
  28. 8 12
      src/main/resources/templates/admin/popup-view.html
  29. 37 23
      src/main/resources/templates/admin/popup-write.html
  30. 52 1
      src/main/resources/templates/main/main.html
  31. 9 6
      src/main/resources/templates/notice/view.html

+ 14 - 0
src/main/java/com/its/web/controller/common/CommonController.java

@@ -10,6 +10,7 @@ import lombok.extern.slf4j.Slf4j;
 import org.springframework.web.bind.annotation.*;
 
 import javax.annotation.Nullable;
+import javax.servlet.http.HttpServletRequest;
 import java.util.List;
 import java.util.Map;
 
@@ -32,4 +33,17 @@ public class CommonController {
     @ResponseBody
     public List<ConnStatisticsDto> getConnStatistics(@Nullable @RequestParam Map<String, String> paramMap) {return this.service.getConnStatistics(paramMap);}
 
+    @ApiOperation(value = "에디터 게시물 이미지 업로드")
+    @PostMapping(value = "/image-upload")
+    @ResponseBody
+    public String noticeImageUpload(HttpServletRequest req) {
+        return this.service.imageUpload(req);
+    }
+
+    @ApiOperation(value= "업로드 이미지 소스 가져오기")
+    @GetMapping(value="/upload/{imageName}")
+    @ResponseBody
+    public byte[] getImage(@PathVariable("imageName") String imageName) {
+        return this.service.getImage(imageName);
+    }
 }

+ 3 - 0
src/main/java/com/its/web/controller/view/ViewController.java

@@ -3,6 +3,7 @@ package com.its.web.controller.view;
 import com.its.web.security.WebPasswordEncoder;
 import com.its.web.service.common.CommonService;
 import com.its.web.service.notice.NoticeService;
+import com.its.web.service.popup.PopupService;
 import com.its.web.service.statistics.StatisticsService;
 import com.its.web.service.traffic.TrafficService;
 import io.swagger.annotations.Api;
@@ -27,6 +28,7 @@ public class ViewController {
     private final StatisticsService statisticsService;
     private final CommonService commonService;
     private final NoticeService noticeService;
+    private final PopupService popupService;
 
     @ApiOperation(value = "01.메인화면")
     @GetMapping("/")
@@ -35,6 +37,7 @@ public class ViewController {
         model.addAttribute("selected", "");
         model.addAttribute("incident", this.trafficService.findMainIncident());
         model.addAttribute("notice", this.noticeService.findMainNotice(3));
+        model.addAttribute("popup", this.popupService.findDisplayPopup());
         return "main/main";
     }
 

+ 2 - 0
src/main/java/com/its/web/mapper/its/popup/PopupMapper.java

@@ -22,4 +22,6 @@ public interface PopupMapper {
     int updatePopup(Map<String, String> paramMap);
 
     int deletePopup(String popupId);
+
+    List<TbWwwPopupDto> findDisplayPopup();
 }

+ 77 - 0
src/main/java/com/its/web/service/common/CommonService.java

@@ -6,10 +6,16 @@ import com.its.web.dto.common.TbWwwOrgDto;
 import com.its.web.mapper.its.common.CommonMapper;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.io.FileUtils;
+import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Service;
 
+import javax.servlet.http.HttpServletRequest;
+import java.io.*;
+import java.net.URLDecoder;
 import java.util.List;
 import java.util.Map;
+import java.util.UUID;
 
 @Slf4j
 @RequiredArgsConstructor
@@ -17,6 +23,9 @@ import java.util.Map;
 public class CommonService {
     private final CommonMapper mapper;
 
+    @Value("${image-location}")
+    String imageLocation;
+
     /**
      * 공공기관 사이트 URL
      * @return List<TbWwwOrgDto>
@@ -55,4 +64,72 @@ public class CommonService {
     public int insertConnHs() { return this.mapper.insertConnHs(); }
 
 
+    public String imageUpload(HttpServletRequest req) {
+        String uploadImageInfo = "";
+        String fileName = req.getHeader("file-name");
+        try {
+            fileName = URLDecoder.decode(fileName, "UTF-8");
+        }
+        catch (UnsupportedEncodingException exception) {
+            log.error("Can not Decoding UTF-8");
+        }
+        String filePath = imageLocation;
+        File file = new File(filePath);
+        boolean isMkdirs = true;
+        if (!file.exists()) {
+            isMkdirs = file.mkdirs();
+        }
+
+        if (isMkdirs) {
+            String realFileNm;
+            realFileNm = UUID.randomUUID().toString().replace("-", "") + fileName.substring(fileName.lastIndexOf("."));
+            String rlFileNm = filePath + File.separator + realFileNm;
+
+            try (
+                    InputStream is = req.getInputStream();
+                    OutputStream os = new FileOutputStream(rlFileNm);
+            ) {
+                int numRead;
+                byte[] byteArray = new byte[Integer.parseInt(req.getHeader("file-size"))];
+                if (is != null) {
+                    while ((numRead = is.read(byteArray, 0, byteArray.length)) != -1) {
+                        os.write(byteArray, 0, numRead);
+                    }
+                }
+                os.flush();
+            } catch (IOException e) {
+                log.error("noticeImgupload() IO Exception");
+            } catch (Exception e1) {
+                log.error("noticeImgupload() Exception");
+            }
+            uploadImageInfo += "&bNewLine=true";
+            uploadImageInfo += "&sFileName=" + fileName;
+            uploadImageInfo += "&sFileURL=" + "/api/common/upload/" + realFileNm;
+        }
+
+        return uploadImageInfo;
+    }
+
+    public byte[] getImage(String imageName) {
+        byte[] image = null;
+        File file = new File(imageLocation, imageName);
+        int fileSize = (int) file.length();
+        if (fileSize > 0) {
+            try {
+                byte[] byteArray = FileUtils.readFileToByteArray(file);
+                if (byteArray.length > 0) {
+                    int ii = 0;
+                    image = new byte[byteArray.length];
+                    for (byte b : byteArray) {
+                        image[ii++] = b;
+                    }
+                }
+            } catch (FileNotFoundException e) {
+                log.error("Not Found File - {}", imageLocation + imageName);
+            } catch (IOException e) {
+                log.error("Can Not Send Image Cause IOException - {}", imageLocation + imageName);
+            }
+        }
+        return image;
+    }
 }

+ 37 - 34
src/main/java/com/its/web/service/popup/PopupService.java

@@ -60,40 +60,39 @@ public class PopupService {
         Map<String, String> resultMap = new HashMap<>();
         String message = "작성하신 팝업 공지가 등록 되었습니다.";
         String success = "S";
-        log.error("fileName : {}", attachFile.getOriginalFilename());
-        if (attachFile != null && !attachFile.isEmpty()) {
-
-            if (attachFile.getOriginalFilename() != null) {
-                String imgName = attachFile.getOriginalFilename();
-                String ext = imgName.substring(imgName.lastIndexOf("."), imgName.length());
-                String fileId = UUID.randomUUID().toString().replaceAll("-", "");
-                fileId += ext;
-                paramMap.put("imgName", imgName);
-                paramMap.put("imgId", fileId);
-                File file = new File(popupLocation);
-
-                if (!file.isDirectory()) {
-                    file.mkdirs();
-                }
-
-                File transFerFile = new File(popupLocation + "/" +  fileId);
-                try {
-                    attachFile.transferTo(transFerFile);
-                } catch (IOException e) {
-                    message = "이미지 업로드 중 오류가 발생했습니다.";
-                    success = "F";
-                    log.error("TransferTo IOException: {}", e.getMessage());
-                }
-            }
-            else {
-                message = "팝업 공지 이미지 정보가 없습니다.";
-                success = "F";
-            }
-        }
-        else {
-            message = "팝업 공지 이미지 정보가 없습니다.";
-            success = "F";
-        }
+//        if (attachFile != null && !attachFile.isEmpty()) {
+//
+//            if (attachFile.getOriginalFilename() != null) {
+//                String imgName = attachFile.getOriginalFilename();
+//                String ext = imgName.substring(imgName.lastIndexOf("."), imgName.length());
+//                String fileId = UUID.randomUUID().toString().replaceAll("-", "");
+//                fileId += ext;
+//                paramMap.put("imgName", imgName);
+//                paramMap.put("imgId", fileId);
+//                File file = new File(popupLocation);
+//
+//                if (!file.exists()) {
+//                    file.mkdirs();
+//                }
+//
+//                File transFerFile = new File(popupLocation + "/" +  fileId);
+//                try {
+//                    attachFile.transferTo(transFerFile);
+//                } catch (IOException e) {
+//                    message = "이미지 업로드 중 오류가 발생했습니다.";
+//                    success = "F";
+//                    log.error("TransferTo IOException: {}", e.getMessage());
+//                }
+//            }
+//            else {
+//                message = "팝업 공지 이미지 정보가 없습니다.";
+//                success = "F";
+//            }
+//        }
+//        else {
+//            message = "팝업 공지 이미지 정보가 없습니다.";
+//            success = "F";
+//        }
 
         if (success.equals("S")) {
             int affectedRow = this.mapper.insertPopup(paramMap);
@@ -207,4 +206,8 @@ public class PopupService {
         resultMap.put("message", message);
         return resultMap;
     }
+
+    public List<TbWwwPopupDto> findDisplayPopup() {
+        return this.mapper.findDisplayPopup();
+    }
 }

+ 2 - 1
src/main/resources/application-dev.yml

@@ -19,4 +19,5 @@ spring:
 
 kakao-url: //dapi.kakao.com/v2/maps/sdk.js?appkey=89c10f45ef100270bc75a54eb9e5b0ca&libraries=drawing&libraries=clusterer
 popup-location: C:\00.PROJECT\24.01.PHITS-WEB\uploads\popup
-board-location: C:\00.PROJECT\24.01.PHITS-WEB\uploads\board
+board-location: C:\00.PROJECT\24.01.PHITS-WEB\uploads\board
+image-location: C:\00.PROJECT\24.01.PHITS-WEB\uploads\images

+ 2 - 1
src/main/resources/application-prod.yml

@@ -18,4 +18,5 @@ spring:
       connectTimeout: 10000
 kakao-url: //dapi.kakao.com/v2/maps/sdk.js?appkey=818515fbf1c2ac66fdac8c66163c7a3e&libraries=drawing&libraries=clusterer
 popup-location: C:\00.PROJECT\24.01.PHITS-WEB\uploads\popup
-board-location: C:\00.PROJECT\24.01.PHITS-WEB\uploads\board
+board-location: C:\00.PROJECT\24.01.PHITS-WEB\uploads\board
+image-location: C:\00.PROJECT\24.01.PHITS-WEB\uploads\images

+ 1 - 2
src/main/resources/mybatis/mapper/itcs/InterSectionMapper.xml

@@ -18,7 +18,7 @@
             , B.ixr_los
             , B.dely_hh
          FROM IXR_MNGM A
-         JOIN IXR_LOS_STTS B
+         LEFT OUTER JOIN IXR_LOS_STTS B
            ON A.ixr_id = B.ixr_id
         WHERE A.use_en = 1
         ORDER BY A.ixr_nm
@@ -52,7 +52,6 @@
           ON CM.ixr_id = ALS.ixr_id
          AND CDM.drct_dvsn_cd = ALS.drct_dvsn_cd
        WHERE CDM.drct_dvsn_cd=ALS.drct_dvsn_cd
-         AND CM.hmpg_dspl_en = 1
     </select>
 
     <select id="findIntersectionAtrdName" resultType="com.its.web.dto.statistics.DaeroMngmDto">

+ 3 - 2
src/main/resources/mybatis/mapper/its/notice/NoticeMapper.xml

@@ -29,7 +29,7 @@
             boardno         as board_no,
             categorycd      as categoryCd,
             parentboardno   as parent_board_no,
-            regdate         as reg_date,
+            TO_CHAR(regdate, 'YYYY-MM-DD') as reg_date,
             bsubject        as b_subject,
             bcontent        as b_content,
             bwriter         as b_writer,
@@ -96,7 +96,8 @@
 
     <update id="updateNotice" parameterType="java.util.HashMap">
         UPDATE TB_WWW_BOARD
-        SET REGDATE      = TO_DATE(TO_CHAR(SYSDATE,'YYYY-MM-DD HH24:MI:SS'), 'YYYY-MM-DD HH24:MI:SS'),
+        SET
+            --REGDATE      = TO_DATE(TO_CHAR(SYSDATE,'YYYY-MM-DD HH24:MI:SS'), 'YYYY-MM-DD HH24:MI:SS'),
             BSUBJECT     = #{bSubject},
             BCONTENT     = #{bContent},
             ATTACHFILE   = #{attachFile},

+ 19 - 0
src/main/resources/mybatis/mapper/its/popup/PopupMapper.xml

@@ -50,6 +50,25 @@
               )
         WHERE ROWNUM BETWEEN 1 AND #{num}
     </select>
+
+    <select id="findDisplayPopup" resultType="com.its.web.dto.admin.TbWwwPopupDto">
+        SELECT
+            popupid
+             , title
+             , url
+             , TO_CHAR(post_from, 'YYYY-MM-DD') as post_from
+             , TO_CHAR(post_to, 'YYYY-MM-DD') as post_to
+             , img_id
+             , img_name
+             , pcontent
+             , TO_CHAR(regdate, 'YYYY-MM-DD') as regdate
+             , use_yn
+             , del_yn
+         FROM TB_WWW_POPUP
+        WHERE use_yn = 'Y'
+          AND del_yn = 'N'
+          AND TO_DATE(sysdate, 'YYYY-MM-DD') BETWEEN TO_DATE(post_from, 'YYYY-MM-DD') AND TO_DATE(post_to, 'YYYY-MM-DD')
+    </select>
     <select id="findPopupTotalPage" resultType="java.lang.Integer">
         SELECT
             count(*)

+ 23 - 2
src/main/resources/static/css/cctv.css

@@ -132,6 +132,27 @@
     color: white;
 }
 
+.state-1::before,
+.state-0::before{
+    content: "";
+    display: inline-block;
+    vertical-align: middle;
+    margin-right: 10px;
+    width: 25px;
+    height: 25px;
+    /*background-image: url("/images/icon/true.png");*/
+    background-size: 25px 25px;
+    background-repeat: no-repeat;
+    background-position: center;
+}
+
+.state-0::before {
+    background-image: url("/images/icon/false.png");
+}
+
+.state-1::before {
+    background-image: url("/images/icon/true.png");
+}
 .cctvWrap .admin-content {
     width: 100%;
     height: calc(100% - 200px);
@@ -230,12 +251,12 @@
 
 .cctvWrap .admin-content .left .list ul > .cmra-list {
     display: none;
-    padding-left: 50px;
+    padding-left: 35px;
 }
 
 .cctvWrap .admin-content .left .list ul > .cmra-list > li {
     padding: 5px;
-    list-style-type: circle;
+    list-style: none;
 }
 
 

+ 3 - 0
src/main/resources/static/css/common.css

@@ -20,6 +20,9 @@
     -webkit-font-smoothing: antialiased;
     -moz-osx-font-smoothing: grayscale;
 }
+.video-js * {
+    font-family: VideoJS !important;
+}
 
 html, body {
     width: 100%;

+ 87 - 8
src/main/resources/static/css/main.css

@@ -3,11 +3,26 @@
 .mainWrap {
     overflow: auto;
     width: 100%;
-    height: calc(100% - 208px);
+    /*height: calc(100% - 208px);*/
     display: flex;
     -webkit-box-pack: center;
     justify-content: center;
-}
+    /*background-image: linear-gradient(rgba(0, 0, 0, 0.1), rgba(0, 0, 0, 0.3)), url(/images/background/main_bg.gif);*/
+    background-image: url(/images/background/main_bg.gif);
+    /*z-index: 1;*/
+}
+/*.mainWrap::after {*/
+/*    content: "";*/
+/*    width: 100%;*/
+/*    position: absolute;*/
+/*    background-image: url(/images/background/bg_main2.jpg);*/
+/*    background-size: cover;*/
+/*    top: 0;*/
+/*    left: 0;*/
+/*    z-index: -1;*/
+/*    opacity: 0.7;*/
+/*    min-height: 1015px;*/
+/*}*/
 
 .mainWrap .first-bg {
     background-image: url(/images/background/bg_cctv.png);
@@ -116,7 +131,6 @@
 body {
     width: 100%;
     height: 100%;
-    background-image: url(/images/background/main_bg.gif);
 }
 
 .mainWrap .main {
@@ -135,13 +149,17 @@ body {
     max-width: 1200px;
     width: 100%;
     position: relative;
-    padding: 50px 0px;
+    /*padding: 50px 0px;*/
 }
 
 .mainWrap .top, .mid {
     display: flex;
 }
 
+.mainWrap .bottom {
+    margin-top: 10px;
+}
+
 .mainWrap .mid > div {
     width: calc(33.3333%);
     height: 220px;
@@ -294,6 +312,45 @@ body {
 .mainWrap .mid a:hover {
     color: #0d6efd;
 }
+.popup_content {
+    width: auto;
+    height: auto;
+    position: absolute;
+    background-color: white;
+    border: 1px solid #7c7b7b;
+    box-shadow: 0 0 5px #000;
+}
+
+.popup_content > div:nth-child(1) {
+    cursor: pointer;
+}
+
+.popup_content > .close-box{
+    background-color: black;
+    color: white;
+    display: flex;
+    justify-content: flex-end;
+    align-items: center;
+    height: 40px;
+    gap: 5px;
+    padding-right: 10px;
+}
+
+.popup_content > .close-box > input,
+.popup_content > .close-box > label,
+.popup_content > .close-box > div {
+    cursor: pointer;
+}
+.popup_content > .close-box > div {
+    margin-left: 5px;
+}
+
+.popup_content {
+    max-width: 320px;
+}
+.popup_content img {
+    max-width: 320px;
+}
 
 @media (max-width: 450px) {
     .mainWrap .top {
@@ -368,7 +425,12 @@ body {
         display: none;
     }
 
-
+    /*.popup_content {*/
+    /*    max-width: 320px;*/
+    /*}*/
+    /*.popup_content img {*/
+    /*    max-width: 320px;*/
+    /*}*/
 }
 
 @media (min-width: 765px) {
@@ -388,6 +450,13 @@ body {
     .mainWrap .notice > div > img {
         display: inline;
     }
+
+    .popup_content {
+        max-width: 600px;
+    }
+    .popup_content img {
+        max-width: 600px;
+    }
 }
 
 @media (min-width: 920px) {
@@ -403,12 +472,22 @@ body {
     .mainWrap .notice > .content > div > span {
         display: inline;
     }
+
+    .popup_content {
+        max-width: 800px;
+    }
+    .popup_content img {
+        max-width: 800px;
+    }
+    .mainWrap {
+        min-height: 758px;
+    }
 }
 
 
 @media (max-height: 900px) {
     /* 메인화면 */
-    .mainWrap .main-menu > .top > div {
-        height: 200px;
-    }
+    /*.mainWrap .main-menu > .top > div {*/
+    /*    height: 200px;*/
+    /*}*/
 }

+ 42 - 31
src/main/resources/static/css/notice.css

@@ -67,10 +67,11 @@
 
 .noticeWrap {
     width: 100%;
-    height: calc(100% - 199.8px);
+    /*height: calc(100% - 199.8px);*/
     display: flex;
     justify-content: center;
     overflow: auto;
+    margin-bottom: 10px;
 }
 .noticeWrap .admin-header {
     width: 200px;
@@ -88,7 +89,7 @@
     position: relative;
     display: flex;
     flex-direction: column;
-    min-height: 700px;
+    min-height: 748px;
 }
 
 .noticeWrap .header {
@@ -173,14 +174,14 @@
 }
 .noticeWrap .content.admin-view {
     width: 100%;
-    height: calc(100% - 128px);
+    /*height: calc(100% - 128px);*/
     min-height: 500px;
     padding: 20px 60px;
     transition: all 0.3s ease 0s;
     background-color: rgb(255, 255, 255);
     box-shadow: rgba(0, 0, 0, 0.15) 0px 3px 6px;
     margin: 0;
-    overflow: auto;
+    /*overflow: auto;*/
 }
 .noticeWrap .admin-content .title {
     padding: 16px 0px;
@@ -276,6 +277,12 @@
 .wt-button:hover{
     filter: brightness(1.1);
 }
+.noticeWrap .content a > div:nth-child(1) {
+    overflow: hidden;
+    text-overflow: ellipsis;
+    white-space: nowrap;
+    width: calc(100% - 160px);
+}
 
 .wt-button{
     background: rgb(204, 234, 234);
@@ -306,6 +313,8 @@
     line-height: inherit;
     outline: none!important;
     min-height: 350px;
+    overflow: auto;
+    max-width: 1080px;
 }
 .noticeWrap .content .view-box .title {
     height: 40px;
@@ -389,7 +398,7 @@
     margin: 0px;
 }
 .item-right {
-    width: 250px;
+    width: 160px;
     display: flex;
     -webkit-box-pack: justify;
     justify-content: space-between;
@@ -416,30 +425,44 @@
     background-position: center;
 }
 @media (max-width: 920px) {
-    .noticeWrap {
-        height: calc(100% - 200.19px);
-    }
+    /*.noticeWrap {*/
+    /*    height: calc(100% - 200.19px);*/
+    /*}*/
 
 }
 
 @media (max-height: 765px) {
-    .noticeWrap {
-        height: calc(100% - 200.19px);
-    }
+    /*.noticeWrap {*/
+    /*    height: calc(100% - 200.19px);*/
+    /*}*/
 }
 
 
 @media (max-width: 720px) {
-    .noticeWrap {
-        height: calc(100% - 205.19px);
+    /*.noticeWrap {*/
+    /*    height: calc(100% - 205.19px);*/
+    /*}*/
+    .noticeWrap .content a > div:nth-child(1) {
+        width: calc(100% - 72px);
+        font-size: 14px;
+    }
+    .item-right{
+        width : 70px;
+        font-size: 12px;
+    }
+    .item-right > div:nth-child(2) {
+        display: none;
+    }
+    .noticeWrap .header{
+        font-size: 1.5rem;
     }
 }
 
 
 @media (max-width: 547px) {
-    .noticeWrap {
-        height: calc(100% - 216.19px);
-    }
+    /*.noticeWrap {*/
+    /*    height: calc(100% - 216.19px);*/
+    /*}*/
 }
 
 @media (max-width: 420px) {
@@ -452,8 +475,8 @@
     }
 
     .noticeWrap {
-        height: calc(100% - 149.19px);
-        padding: 5px 0;
+        /*height: calc(100% - 149.19px);*/
+        padding: 5px 0 78px 0;
     }
 
     .noticeWrap .header {
@@ -477,19 +500,7 @@
         font-size: 13px;
     }
 
-    .noticeWrap .content a > div:nth-child(1) {
-        overflow: hidden;
-        text-overflow: ellipsis;
-        white-space: nowrap;
-        width: calc(100% - 135px);
-    }
-    .item-right{
-        width : 122px;
-        font-size: 12px;
-    }
-    .item-right > div:nth-child(2) {
-        display: none;
-    }
+
     /*textarea {*/
     /*    row*/
     /*}*/

+ 7 - 0
src/main/resources/static/css/traffic.css

@@ -404,6 +404,13 @@ ul, li {
     border-bottom: 1px solid #c7c6c6;
 }
 
+.incident-info-window .title > div:nth-child(1),
+.vms-info-window .title > div:nth-child(1),
+.cctv-info-window .title > div:nth-child(1) {
+    width: calc(100% - 30px);
+    height: 100%;
+}
+
 .incident-info-window .title > div:nth-child(2),
 .vms-info-window .title > div:nth-child(2),
 .cctv-info-window .title > div:nth-child(2) {

BIN
src/main/resources/static/images/background/bg_main2.jpg


BIN
src/main/resources/static/images/background/bottom_img.jpg


BIN
src/main/resources/static/images/icon/false.png


BIN
src/main/resources/static/images/icon/true.png


+ 42 - 0
src/main/resources/static/js/common/common.js

@@ -55,4 +55,46 @@ function dateFormatter(date) {
     month = month < 10 ? "0" + month : month;
     day   = day < 10 ? "0" + day : day;
     return year + month + day;
+}
+
+function getCookie(name) {
+    let matches = document.cookie.match(new RegExp(
+        "(?:^|; )" + name.replace(/([\.$?*|{}\(\)\[\]\\\/\+^])/g, '\\$1') + "=([^;]*)"
+    ));
+    return matches ? decodeURIComponent(matches[1]) : undefined;
+}
+
+function setCookie(name, value, options = {}) {
+
+    options = {
+        path: '/', // 경로 지정
+        ...options // 아규먼트로 옵션을 넘겨줬을경우 전개연산자로 추가 갱신
+    };
+
+    if (options.expires instanceof Date) {
+        options.expires = options.expires.toUTCString(); // 생 Date 객체라면 형식에 맞게 인코딩
+    }
+
+    let updatedCookie = encodeURIComponent(name) + "=" + encodeURIComponent(value);
+
+    for (let optionKey in options) {
+        updatedCookie += "; " + optionKey;
+        let optionValue = options[optionKey];
+        if (optionValue !== true) { // 밸류가 없다면
+            updatedCookie += "=" + optionValue;
+        }
+    }
+    console.log(updatedCookie);
+    document.cookie = updatedCookie; // 새로 갱신
+}
+
+function closePopup(popupId) {
+    const isChecked = $('#popup_check_' + popupId).is(':checked');
+    if (isChecked) {
+        let expire = new Date();
+        expire.setDate(expire.getDate() + 1);
+        console.log(expire);
+        setCookie('p_'+ popupId , 'N', {expires : expire});
+    }
+    $('#popup_' + popupId).css('display', 'none');
 }

+ 1 - 0
src/main/resources/static/js/naverEditor/SmartEditor2Skin.html

@@ -13,6 +13,7 @@
 <script type="text/javascript" src="./js/lib/jindo_component.js" charset="utf-8"></script>
 <script type="text/javascript" src="./js/SE2M_Configuration.js" charset="utf-8"></script>	<!-- 설정 파일 -->
 <script type="text/javascript" src="./js/SE2BasicCreator.js" charset="utf-8"></script>
+<script type="text/javascript" src="./js/SE_QuickEditor_Image.js" charset="utf-8"></script>
 <script type="text/javascript" src="./js/smarteditor2.min.js" charset="utf-8"></script>
 
 <!-- 사진첨부샘플  --> 

+ 1 - 0
src/main/resources/static/js/naverEditor/js/SE2BasicCreator.js

@@ -21,6 +21,7 @@ function createSEditor2(elIRField, htParams, elSeAppContainer){
 	var oEditor = new nhn.husky.HuskyCore(htParams);
 	oEditor.registerPlugin(new nhn.husky.CorePlugin(htParams?htParams.fOnAppLoad:null));	
 	oEditor.registerPlugin(new nhn.husky.StringConverterManager());
+	oEditor.registerPlugin(new nhn.husky.SE_QuickEditor_Image(elAppContainer));
 
 	var htDimension = {
 		nMinHeight:205,

+ 10 - 0
src/main/resources/static/js/naverEditor/js/SE2M_Configuration.js

@@ -49,4 +49,14 @@ nhn.husky.SE2M_Configuration.Quote = {
 };
 nhn.husky.SE2M_Configuration.SE2M_ColorPalette = {
 	bAddRecentColorFromDefault : false
+};
+
+nhn.husky.SE2M_Configuration.QuickEditor = {
+	common : {
+		bUseConfig : false
+	},
+	Image : {
+		nImageMaxWidthSize : 9999,
+		nImageMaxHeightSize : 9999
+	}
 };

+ 483 - 0
src/main/resources/static/js/naverEditor/js/SE_QuickEditor_Image.js

@@ -0,0 +1,483 @@
+/**
+ * @name SE_QuickEditor_Image
+ * @description Quick Editor Image function Class
+ * @author NHN AjaxUI Lab - cielo
+ * @version 1.0
+ * @since 2009.10
+ */
+nhn.husky.SE_QuickEditor_Image = jindo.$Class({
+    name : "SE_QuickEditor_Image",
+    _aQEImg : [],
+    elCurrentFocus : "",
+    nMaxWidth : Infinity,
+    nMaxHeight : Infinity,
+
+    $init : function(elAppContainer){
+    },
+
+    $ON_MSG_APP_READY : function(){
+        this.elEventTarget = null;
+        this.elElement = null;
+        this.welElement = null;
+        this.nMaxWidth = (nhn.husky.SE2M_Configuration.QuickEditor.Image.nImageMaxWidthSize || Infinity);
+        this.nMaxHeight = (nhn.husky.SE2M_Configuration.QuickEditor.Image.nImageMaxHeightSize || Infinity);
+        this.bImgAutoAdjust = true;	/* true : 이미지의 가로/세로 비율을 유지함, false : 이미지의 가로/세로 비율을 유지하지 않고 각각 조절 가능함 */
+    },
+
+    _assignHTMLObjects : function(elAppContainer){
+        this.oApp.exec("LOAD_HTML", ["qe_image"]);
+        var self = this;
+
+        this.elQELayer = jindo.$$.getSingle(".q_img_wrap", elAppContainer);
+        this.elQELayer.style.zIndex = 150;
+        this.elBtnBGPalette = jindo.$$.getSingle(".husky_se2m_img_qe_bgcolor_btn", this.elQELayer);
+        this.elBtnResetPalette = jindo.$$.getSingle(".se2_sreset", this.elQELayer);
+        this.elPanelBGPaletteHolder = jindo.$$.getSingle(".husky_se2m_img_qe_bg_paletteHolder", this.elQELayer);
+        this.elPanelAlign0 = jindo.$$.getSingle(".se2_align0", this.elQELayer);
+        this.elPanelAlign1 = jindo.$$.getSingle(".se2_align1", this.elQELayer);
+        this.elPanelAlign2 = jindo.$$.getSingle(".se2_align2", this.elQELayer);
+        this.elPanelImgWidth = jindo.$$.getSingle(".widthimg", this.elQELayer);
+        this.elPanelImgHeight = jindo.$$.getSingle(".heightimg", this.elQELayer);
+        this.elCheckImgAutoAdjust = jindo.$$.getSingle(".se2_srate", this.elQELayer);	/* 이미지의 가로/세로 길이를 따로따로 설정할 수 있도록 함 */
+        this.elCheckImgAutoAdjust.checked = true;										/* 기본값은 checked (-> 가로 세로 비율 유지함, 가로 길이가 줄 때 세로 길이도 비율에 맞추어 줄어듬) */
+        this.elPanelImgBorder = jindo.$$.getSingle(".bordersize", this.elQELayer);
+        this.elPanelHighEdit = jindo.$$.getSingle(".se2_highedit", this.elQELayer);
+
+        new jindo.NumericStepper(jindo.$$.getSingle(".se2_numberStepper", this.elQELayer), {
+            sClassPrefix : '',
+            nStep : 1, 					// (Number) 가감(+/-)이  일어나는 단위를 의미합니다.
+            nMax : 10,	 				// (Number) 최대값의 제한값이 설정됩니다.
+            nMin : 0, 					// (Number) 최소값의 제한 값이 설정됩니다.
+            nDefaultValue : 0,			// (Number) Input Box안에 들어갈 default value
+            bUseMouseWheel : false,		// 마우스 휠을 사용하여, input vale를 컨트할 수 있게 할지 여부 세팅
+            bInputReadOnly : true		// Input Box의 속성을 결정하는 option
+        }).attach({
+            beforeChange : function(oCustomEvent){
+                if(oCustomEvent.nValue > oCustomEvent.nMax || oCustomEvent.nValue < oCustomEvent.nMin){
+                    oCustomEvent.stop();
+                }
+            },
+            change : function(oCustomEvent){
+                self.oApp.exec("IMG_QE_SET_BORDER", [oCustomEvent.nValue]);
+            }
+        });
+    },
+
+    $BEFORE_EXECCOMMAND : function(){
+        this.oApp.exec("CLOSE_QE_LAYER");
+    },
+
+    $BEFORE_CHANGE_EDITING_MODE : function(sMode, bNoFocus){
+        if(sMode !== "WYSIWYG"){
+            this.welElement = null;
+            this.oApp.exec("CLOSE_QE_LAYER");
+        }
+    },
+
+    _hasThumbnail : function(id) {
+        return this.oApp.hasThumbnail && this.oApp.hasThumbnail(id);
+    },
+
+    /**
+     * 퀵에디터 판넬의 각 버튼에 이벤트 설정
+     */
+    $LOCAL_BEFORE_FIRST : function(sMsg){
+        // CHANGE_EDITING_MODE 시에 불필요한 이벤트 등록을 피하기 위해 추가된 코드임
+        if(!!sMsg.match(/(REGISTER_CONVERTERS|CHANGE_EDITING_MODE)/)){
+            this.oApp.acceptLocalBeforeFirstAgain(this, true);
+            return true;
+        }
+        this._assignHTMLObjects(this.oApp.htOptions.elAppContainer);
+        this.oApp.registerBrowserEvent(this.elBtnBGPalette, "click", "IMG_QE_TOGGLE_BGC_PALETTE");
+        this.oApp.registerBrowserEvent(this.elBtnResetPalette, "click", "IMG_QE_RESET_PALETTE");
+
+        this.oApp.registerBrowserEvent(this.elPanelAlign0, "click", "IMG_QE_ALIGN_PALETTE");
+        this.oApp.registerBrowserEvent(this.elPanelAlign1, "click", "IMG_QE_ALIGN_PALETTE");
+        this.oApp.registerBrowserEvent(this.elPanelAlign2, "click", "IMG_QE_ALIGN_PALETTE");
+
+        this.oApp.registerBrowserEvent(this.elPanelImgWidth, "blur", "IMG_QE_RESIZE_PALETTE");
+        this.oApp.registerBrowserEvent(this.elPanelImgHeight, "blur", "IMG_QE_RESIZE_PALETTE");
+        this.oApp.registerBrowserEvent(this.elPanelImgWidth, "focus", "IMG_QE_RESIZE_START_PALETTE");
+        this.oApp.registerBrowserEvent(this.elPanelImgHeight, "focus", "IMG_QE_RESIZE_START_PALETTE");
+        this.oApp.registerBrowserEvent(this.elPanelImgWidth, "mousedown", "IMG_QE_RESIZE_START_PALETTE");
+        this.oApp.registerBrowserEvent(this.elPanelImgHeight, "mousedown", "IMG_QE_RESIZE_START_PALETTE");
+        this.oApp.registerBrowserEvent(this.elCheckImgAutoAdjust, "click", "IMG_SIZE_ADJUST");
+        this.oApp.registerBrowserEvent(this.elPanelHighEdit, "click", "HIGH_EDIT_CLICK");
+    },
+
+    $ON_EVENT_EDITING_AREA_DBLCLICK : function(ev){
+        var elDBClickEventTarget = ev.element;
+        if(elDBClickEventTarget.tagName != "IMG"){
+            return;
+        }
+
+        if(this._hasThumbnail(elDBClickEventTarget.id)){
+            this.oApp.exec("CLOSE_QE_LAYER"); // 퀵에디터 닫음
+        }
+    },
+
+    $ON_HIGH_EDIT_CLICK : function(){
+        if(!!this.welElement && this._hasThumbnail(this.elElement.id) && this.welElement.attr("s_subtype") == "photo"){
+            this.oApp.exec("IMG_SE_REEDIT_PHOTO", [this.welElement]);
+        }
+    },
+
+    $ON_EVENT_EDITING_AREA_MOUSEDOWN : function(ev){
+        this.elEventTarget = null;
+
+        if(ev.element.tagName != null && (ev.element.tagName).toLowerCase() == 'img'){
+            this.elEventTarget = ev.element;
+        }
+    },
+
+    $ON_EVENT_EDITING_AREA_MOUSEUP : function(ev){
+        if(!!this.elEventTarget && this.elEventTarget == ev.element){
+            this._mouseUp_WYSIWYGDoc(ev);
+        }else{
+            this.elEventTarget = null;
+        }
+    },
+
+    $ON_REGISTER_CONVERTERS : function(){
+        this.oApp.exec("ADD_CONVERTER", ["IR_TO_DB", jindo.$Fn(this.irToDB, this).bind()]);
+    },
+
+    irToDB : function(sHtml){
+        // irToDB시점에 임의로 추가한 속성을 제거한다.
+        sHtml = sHtml.replace(/sQEId\s*=\s*\"([^>"]*)\"/i,'');
+        // 'element의 초기속성'을 담은 array를 초기화
+        this._aQEImg = [];
+        return sHtml;
+    },
+
+    _mouseUp_WYSIWYGDoc : function(wevE){
+        if(!wevE.element || 'IMG' != wevE.element.tagName){
+            return;
+        }
+
+        // 특정 이미지만 퀵 에디터를 사용할 수 없음!
+        this.elElement = wevE.element;
+        this.welElement = jindo.$Element(wevE.element);
+
+        if('false' == this.welElement.attr("imgqe")){
+            return;
+        }
+
+        // 기본 bordercolor를 셋팅
+        this.elElement.style.borderColor = this.elElement.style.borderLeftColor || "rgb(0, 0, 0)";
+
+        // sQEId 셋팅
+        var sQEId = this.welElement.attr("sQEId");
+        if('undefined' == typeof(sQEId) || !sQEId){
+            sQEId = nhn.husky.SE2M_Utils.getUniqueId("QE_");
+            this._aQEImg[sQEId] = this._getInfoImage(this.elElement);
+            this.welElement.attr("sQEId", sQEId); // key생성
+        } else if("undefined" == typeof ( this._aQEImg[sQEId] ) || !this._aQEImg[sQEId] ) {
+            //[SMARTEDITORSUS-356] 임시저장 이후 비워진 array채우는 역할.
+            // 에디터 초기화시 call되는 func이 생기면 그쪽으로 리팩토링이 필요함.
+            this._aQEImg[sQEId] = this._getInfoImage(this.elElement);
+        }
+
+        this._panelReSetting();
+        this._hideImagePalette();
+
+        this.oApp.exec("OPEN_QE_LAYER", [this.elElement, this.elQELayer, "img"]);
+    },
+
+    /**
+     * 이미지 정보를 저장
+     * @param {Object} elElement
+     */
+    _getInfoImage : function(elElement){
+        var htImageInfo = {
+            width : elElement.width,
+            height : elElement.height,
+            rwidth : this.welElement.attr("rwidth") || '', // 삽입 시의 이미지 width
+            rheight : this.welElement.attr("rheight") || '', // 삽입 시의 이미지 height
+            border : elElement.border || 0,
+            borderColor : elElement.style.borderLeftColor || "rgb(0, 0, 0)" ,
+            align : elElement.align || ""
+        };
+        return htImageInfo;
+    },
+
+    _panelReSetting : function(){
+        var elImage = this.elElement;
+        this.elPanelImgBorder.value = (this.welElement.attr("border") || 0);
+        this.elPanelImgWidth.value = (elImage.width || this.welElement.width());
+        this.elPanelImgHeight.value = (elImage.height || this.welElement.height());
+        this.elBtnBGPalette.style.backgroundColor = elImage.style.borderLeftColor || "rgb(0, 0, 0)";
+
+        // 고급편집 버튼 보여주기 여부 체크 (이미지 업로드로 올린 사진 && 첨부영역에 썸네일이 있어야 함)
+        if(this._hasThumbnail(elImage.id) && this.welElement.attr("s_subtype") == "photo"){
+            jindo.$Element(this.elPanelHighEdit).show();
+        }else{
+            jindo.$Element(this.elPanelHighEdit).hide();
+        }
+    },
+
+    _hideImagePalette : function(){
+        this.elPanelBGPaletteHolder.parentNode.style.display = "none";
+        this.oApp.exec("HIDE_COLOR_PALETTE");
+    },
+
+    /**
+     * 초기화 버튼 클릭시 사용
+     * @param {Event} weEvent
+     */
+    $ON_IMG_QE_RESET_PALETTE : function(weEvent){
+        var sQEId = this.welElement.attr("sQEId");
+        var elTemp = this._aQEImg[sQEId];
+        if(!!elTemp){
+            var htImageSize = {
+                "width" : elTemp.width,
+                "height" : elTemp.height,
+                "rwidth" : elTemp.rwidth,
+                "rheight" : elTemp.rheight
+            };
+            this.welElement.css("borderWidth", elTemp.border+"px");
+            this.welElement.css("borderStyle", "solid");
+            this.welElement.css("borderColor", elTemp.borderColor);
+            this.welElement.attr("border", elTemp.border);
+            this.welElement.attr("align", elTemp.align);
+            this.welElement.attr("rwidth", elTemp.rwidth);
+            this.welElement.attr("rheight", elTemp.rheight);
+            this._changeSizeValue(htImageSize);
+        }
+
+        this._panelReSetting();
+    },
+
+    /**
+     * 정비례하게 증감
+     * @param {Event} weEvent
+     */
+    $ON_IMG_QE_RESIZE_START_PALETTE : function(weEvent){
+        var elCurrentFocus = jindo.$Element(weEvent.element);
+        if(elCurrentFocus.isEqual(this.elPanelImgWidth)){
+            this.elPanelImgWidth.readOnly = false;
+            this.elPanelImgHeight.readOnly = true;
+        }else{
+            this.elPanelImgWidth.readOnly = true;
+            this.elPanelImgHeight.readOnly = false;
+        }
+    },
+
+    $ON_IMG_QE_ENROLL_ATTR : function(welQEImg){
+        welQEImg.attr("imgqe",true);
+    },
+
+    $ON_IMG_QE_RESIZE_PALETTE : function(weEvent){
+        this._resizeImage(weEvent.element);
+        weEvent.stop();
+    },
+
+    _resizeImage : function(elEvent){
+        var elImage = this.elElement;
+        var elCurrentFocus = jindo.$Element(elEvent);
+        var nImgAfterWidth = this.elPanelImgWidth.value;
+        var nImgAfterHeight = this.elPanelImgHeight.value;
+        var nImgBeforeWidth = (elImage.width || this.welElement.width());
+        var nImgBeforeHeight = (elImage.height || this.welElement.height());
+        var nReserveWidth = this.welElement.attr("rwidth");
+        var nReserveHeight = this.welElement.attr("rheight");
+        var nTemp;
+
+        if(elCurrentFocus.isEqual(this.elPanelImgWidth)){ // change width
+            if(this.nMaxWidth > nImgAfterWidth && nImgAfterWidth > 0){
+                nTemp = nImgAfterWidth/nImgBeforeWidth;
+                var htImageSize = {
+                    nAdjustWidth : Number(nImgAfterWidth),
+                    nAdjustHeight : Number(nImgBeforeHeight * nTemp),
+                    nImgBeforeWidth : nImgBeforeWidth,
+                    nImgBeforeHeight : nImgBeforeHeight,
+                    nReserveWidth : nReserveWidth,
+                    nReserveHeight : nReserveHeight
+                };
+
+                if(this.bImgAutoAdjust){					/* 가로/세로 비율 유지하면서 사이즈 조절 */
+                    var htAdjustImageSize = this._adjustViewImageSize(htImageSize);
+                    this._changeSizeValue(htAdjustImageSize);
+                }else{
+                    var htAdjustSize = this._adjustViewImageWidth(htImageSize);
+                    this._changeWidth(htAdjustSize);
+                }
+            }
+        }else{ // change height
+            if(this.nMaxHeight > nImgAfterHeight && nImgAfterHeight > 0){
+                nTemp = nImgAfterHeight/nImgBeforeHeight;
+                var htImageSize = {
+                    nAdjustWidth : Number(nImgBeforeWidth * nTemp),
+                    nAdjustHeight : Number(nImgAfterHeight),
+                    nImgBeforeWidth : nImgBeforeWidth,
+                    nImgBeforeHeight : nImgBeforeHeight,
+                    nReserveWidth : nReserveWidth,
+                    nReserveHeight : nReserveHeight
+                };
+
+                if(this.bImgAutoAdjust){					/* 가로/세로 비율 유지하면서 사이즈 조절 */
+                    var htAdjustImageSize = this._adjustViewImageSize(htImageSize);
+                    this._changeSizeValue(htAdjustImageSize);
+                }else{
+                    this.welElement.attr("noadjust", true);
+                    this._changeHeight(htImageSize);
+                }
+            }
+        }
+
+        // 속성 width와 height보다 style의 width와 height 우선시 되기 때문에 둘다 변경해 준다.
+        this.oApp.exec("OPEN_QE_LAYER", [elImage, this.elQELayer, "img"]);
+        this._panelReSetting();
+    },
+
+    _changeSizeValue : function(htSize){
+        var elImage = this.elElement;
+        elImage.width = htSize.width;
+        elImage.style.width = htSize.width + "px";
+        elImage.height = htSize.height;
+        elImage.style.height = htSize.height + "px";
+
+        // 이미지 퀵 에디터에서 설정한 이미지의 width/height 를 이미지에 적용 ([SMARTEDITORSUS-279] related)
+        this.welElement.attr("rwidth", htSize.rwidth);
+        this.welElement.css("rwidth", htSize.rwidth + "px");
+        this.welElement.attr("rheight", htSize.rheight);
+        this.welElement.css("rheight", htSize.rheight+ "px");
+    },
+
+    _changeWidth : function(htSize){
+        var elImage = this.elElement;
+        elImage.width = htSize.width;
+        elImage.style.width = htSize.width + "px";
+        elImage.height = htSize.height;
+        elImage.style.height = htSize.height + "px";
+
+        // 이미지 퀵 에디터에서 설정한 이미지의 width 를 이미지에 적용, height 유지 ([SMARTEDITORSUS-279] related)
+        this.welElement.attr("rwidth", htSize.rwidth);
+        this.welElement.css("rwidth", htSize.rwidth + "px");
+        this.welElement.attr("height", htSize.height);
+        this.welElement.css("height", htSize.height + "px");
+    },
+
+    _changeHeight : function(htSize){
+        var elImage = this.elElement;
+        elImage.height = htSize.nAdjustHeight;
+        elImage.style.height = htSize.nAdjustHeight + "px";
+
+        // 이미지 퀵 에디터에서 설정한 이미지의 height 를 이미지에 적용 ([SMARTEDITORSUS-279] related)
+        this.welElement.attr("rheight", htSize.nAdjustHeight);
+        this.welElement.css("rheight", htSize.nAdjustHeight + "px");
+        this.welElement.attr("rwidth", htSize.nReserveWidth);
+        this.welElement.css("rwidth", htSize.nReserveWidth + "px");
+    },
+
+    /*
+     * [SMARTEDITORSUS-258, 276, 279]
+     *		1) 에디터 가로폭 개선에 따라 이미지 첨부 시 가로폭 크기에 맞는 이미지의 사이즈 조절이 필요함
+     *		2) 에디터 가로폭 꺽쇠가 노출되는 경우에만 에디터 본문 안에 보이는 이미지의 사이즈를 조절함
+     *		3) 사용자가 조절하는 경우(포토업로더, 이미지 퀵 에디터에서 변경) 이외에는 이미지의 사이즈를 조절하지 않음
+     * [SMARTEDITORSUS-1858] 가로폭 꺽쇠 적용과 상관없이 큰 이미지는 에디터 가로폭에 맞게 리사이즈되도록 변경
+     */
+    _adjustViewImageSize : function(htSize){
+        var htImageSize = {
+            "width" : htSize.nAdjustWidth,
+            "height" : htSize.nAdjustHeight,
+            "rwidth" : htSize.nAdjustWidth,
+            "rheight" : htSize.nAdjustHeight
+        };
+
+        var welWysiwygBody = jindo.$Element(this.oApp.getWYSIWYGDocument().body);
+        if(welWysiwygBody){
+            var nEditorWidth = welWysiwygBody.width();
+            if(htSize.nAdjustWidth > nEditorWidth){
+                htImageSize.width = htSize.nImgBeforeWidth;
+                htImageSize.height = htSize.nImgBeforeHeight;
+                htImageSize.rwidth = htSize.nReserveWidth;
+                htImageSize.rheight = htSize.nReserveHeight;
+                var msg = this.oApp.$MSG("SE_QuickEditor_Image.exceedMaxSize").replace("${nEditorWidth}", nEditorWidth);
+                alert(msg);
+            }
+        }
+        return htImageSize;
+    },
+
+    _adjustViewImageWidth : function(htSize){
+        var htImageSize = {
+            "width" : htSize.nAdjustWidth,
+            "height" : htSize.nImgBeforeHeight,
+            "rwidth" : htSize.nAdjustWidth,
+            "rheight" : htSize.nReserveHeight
+        };
+
+        var welWysiwygBody = jindo.$Element(this.oApp.getWYSIWYGDocument().body);
+        if(welWysiwygBody){
+            var nEditorWidth = welWysiwygBody.width();
+            if(htSize.nAdjustWidth > nEditorWidth){
+                htImageSize.width = htSize.nImgBeforeWidth;
+                htImageSize.rwidth = htSize.nReserverWidth;
+                var msg = this.oApp.$MSG("SE_QuickEditor_Image.exceedMaxSize").replace("${nEditorWidth}", nEditorWidth);
+                alert(msg);
+            }
+        }
+        return htImageSize;
+    },
+
+    /**
+     * '가로 세로 비율 유지' 옵션 추가 ([SMARTEDITORSUS-150] related)
+     * 1) 옵션 체크 : 첨부한 사진의 가로나 세로의 길이를 변경할 경우 고정 비율로 이미지 사이즈가 조절됨 (this.bImgAutoAdjust -> true)
+     * 2) 옵션 체크 해제 : 첨부한 사진의 가로/세로 길이를 따로따로 설정할 수 있음 (this.bImgAutoAdjust -> false)
+     */
+    $ON_IMG_SIZE_ADJUST : function(){
+        this.bImgAutoAdjust = (this.elCheckImgAutoAdjust.checked) ? true : false;
+    },
+
+    $ON_CLOSE_SUB_LAYER_QE : function(){
+        if(typeof this.elPanelBGPaletteHolder != 'undefined'){
+            this.elPanelBGPaletteHolder.parentNode.style.display = "none";
+        }
+        if(typeof this.elPanelBGIMGPaletteHolder != 'undefined'){
+            this.elPanelBGIMGPaletteHolder.parentNode.style.display = "none";
+        }
+    },
+
+    $ON_IMG_QE_TOGGLE_BGC_PALETTE : function(){ // border BGC 버튼 토글
+        if(this.elPanelBGPaletteHolder.parentNode.style.display == "block"){
+            this._hideImagePalette();
+        }else{
+            this._showImagePalette();
+        }
+    },
+
+    $ON_IMG_QE_ALIGN_PALETTE : function(wevE){
+        var welButtonEvent = jindo.$Element(wevE.element); // Click이 일어난 정렬 버튼
+        if(welButtonEvent.hasClass("left")){
+            this.welElement.attr("align", "left");
+            this.welElement.css("clear", "left");
+        }else if(welButtonEvent.hasClass("right")){
+            this.welElement.attr("align", "right");
+            this.welElement.css("clear", "right");
+        }else{
+            this.welElement.attr("align", "");
+            this.welElement.css("clear", "both");
+        }
+    },
+
+    $ON_IMG_QE_SET_BORDER : function(nBorderSize){  // border size
+        this.welElement.css("borderWidth", nBorderSize + "px");
+        this.welElement.css("borderStyle", "solid");
+        this.welElement.attr("border", nBorderSize);
+        this.elElement.border = nBorderSize;
+    },
+
+    $ON_IMG_QE_SET_BGC_FROM_PALETTE : function(sColorCode){ // border color
+        this.elBtnBGPalette.style.backgroundColor = sColorCode;
+        this.elElement.style.borderColor = sColorCode;
+        if(this.elPanelBGPaletteHolder.parentNode.style.display == "block"){
+            this._hideImagePalette();
+        }
+    },
+
+    _showImagePalette : function(){
+        this.elPanelBGPaletteHolder.parentNode.style.display = "block";
+        this.oApp.exec("SHOW_COLOR_PALETTE", ["IMG_QE_SET_BGC_FROM_PALETTE", this.elPanelBGPaletteHolder]);
+    }
+});

+ 1 - 1
src/main/resources/static/js/naverEditor/sample/photo_uploader/attach_photo.js

@@ -333,7 +333,7 @@
     function html5Upload() {
     	var tempFile,
     		sUploadURL;
-    	sUploadURL= '/api/notice/image-upload'; 	//upload URL
+    	sUploadURL= '/api/common/image-upload'; 	//upload URL
     	
     	//파일을 하나씩 보내고, 결과를 받음.
     	for(var j=0, k=0; j < nImageInfoCnt; j++) {

+ 14 - 12
src/main/resources/static/js/traffic/traffic.js

@@ -979,7 +979,7 @@ class TbCCtvObj {
         const iwContent =
             `<div class="cctv-info-window">
                     <div class="title">
-                        <div>${this.NAME}</div>
+                        <div class="cctv-name-${this.ID}">${this.NAME}</div>
                         <div onclick="infoWindowEvent('cctv', '${this.ID}', 'close')">X</div>
                     </div>
                     <div class="content">
@@ -1001,7 +1001,7 @@ class TbCCtvObj {
             left : left + 'px',
             position : 'absolute'
         });
-        this.infoWindow.draggable({containment : 'body'});
+        this.infoWindow.draggable({containment : 'body', handle: '.cctv-name-'+this.ID});
         // this.infoWindow = new kakao.maps.CustomOverlay({position: coordinates, content: iwContent, zindex: 15, yAnchor: 1.1});
         // this.infoWindow.setMap(_Map);
         let video = videojs("video-" + this.ID, {
@@ -1164,7 +1164,7 @@ class TbVmsObj {
         let iwContent =
             `<div class="vms-info-window" style="width: calc(${width} + 10px); height: calc(${height} + 50px);">
                     <div class="title">
-                        <div>${this.NAME}</div>
+                        <div class="vms-name-${this.ID}">${this.NAME}</div>
                         <div onclick="infoWindowEvent('vms', ${this.ID}, 'close')">X</div>
                     </div>
                     <div class="content" style="width: ${width}; height: ${height};">`;
@@ -1198,7 +1198,7 @@ class TbVmsObj {
             position: 'absolute',
             zIndex : '999',
         })
-        this.infoWindow.draggable({containment : 'body'});
+        this.infoWindow.draggable({containment : 'body', handle: '.vms-name-'+this.ID});
         // this.infoWindow.setMap(_Map);
         setMarkerImage(this, 2)
 
@@ -1288,7 +1288,7 @@ class TbIncdObj {
         let iwContent =
             `<div class="incident-info-window">
                     <div class="title">
-                        <div>${this.NAME}</div>
+                        <div class="incident-name-${this.ID}">${this.NAME}</div>
                         <div onclick="infoWindowEvent('incident', '${this.ID}', 'close')">X</div>
                     </div>
                     <div class="content">
@@ -1312,14 +1312,13 @@ class TbIncdObj {
         const wrapH     = $('.trafficWrap').innerHeight();
         let top = headerH + (wrapH/2) - this.infoWindow.innerHeight() + 10;
         let left = (window.innerWidth / 2) - (this.infoWindow.innerWidth() / 2);
-        console.log(this.infoWindow.width());
         this.infoWindow.css({
             top : top + 'px',
             left : left + 'px',
             position: 'absolute',
             zIndex : '999',
         })
-        this.infoWindow.draggable({containment : 'body'});
+        this.infoWindow.draggable({containment : 'body', handle: 'incident-name-'+ this.ID});
 
 
         setMarkerImage(this, 2);
@@ -1471,7 +1470,7 @@ class IntersectionCameraObj {
         const iwContent =
             `<div class="cctv-info-window">
                     <div class="title">
-                        <div>${this.NAME}</div>
+                        <div class="ixr-name-${this.ID}">${this.NAME}</div>
                         <div class="close-window">X</div>
                     </div>
                     <div class="content">
@@ -1497,7 +1496,7 @@ class IntersectionCameraObj {
             zIndex : '999',
         });
 
-        this.infoWindow.draggable({content : 'body'});
+        this.infoWindow.draggable({content : 'body', handle : 'ixr-name-' + this.ID});
 
         // this.infoWindow = new kakao.maps.CustomOverlay({
         //     position: coordinates,
@@ -1891,13 +1890,16 @@ function receiveFacilityData(jsonData, array, facilityClass, listFlag, type) {
     let listStr = "";
     let mobileStr = "";
     jsonData.forEach((obj)=>{
+        console.log(obj);
         const markerObj = new facilityClass(obj);
         if (type === 'intersection') {
             if (obj.detail && obj.detail.length > 0) {
                 obj.detail.forEach((cameraObj)=>{
-                    const camera = new IntersectionCameraObj(cameraObj);
-                    camera.init();
-                    _IntersectionCameraArray.push(camera);
+                    if (cameraObj.cmra_use_yn ===1) {
+                        const camera = new IntersectionCameraObj(cameraObj);
+                        camera.init();
+                        _IntersectionCameraArray.push(camera);
+                    }
                 })
             }
         }

+ 18 - 8
src/main/resources/templates/admin/cctv.html

@@ -9,8 +9,8 @@
     <title>포항시 교통정보센터</title>
     <th:block th:include="/include/head.html"></th:block>
     <link rel="stylesheet" th:href="@{/css/cctv.css}">
-    <link rel="stylesheet" th:href="@{/css/video-7.10.2.css}">
     <script th:src="@{/js/videojs/video-7.10.2.js}"></script>
+    <link rel="stylesheet" th:href="@{/css/video-7.10.2.css}">
 </head>
 <body id="body">
 <th:block th:include="/include/admin-header.html"></th:block>
@@ -98,6 +98,10 @@
             autoplay: true,
             muted: true,
             preload: "metadata",
+            controlBar : {
+                pictureInPictureToggle: false,
+                volumePanel: false,
+            }
         })
         timer = setTimeout(()=>{
             video.pause();
@@ -122,14 +126,20 @@
         });
 
         intersection.forEach((obj)=>{
-            intersectionStr += `<ul id="${obj.ixr_id}" onclick="ixrClick('${obj.ixr_id}')">
-                                    <div>${obj.ixr_nm}</div>
-                                    <ul class="cmra-list">`
-            ixrMap.set(obj.ixr_id, obj);
+            let cnt = 0;
+            let detailStr = "";
             obj.detail.forEach((camera)=>{
-                intersectionStr += `<li id="${camera.cmra_id}" onclick="detailClick('${camera.cmra_id}')">${camera.drct_lctn}</li>`;
+                detailStr += `<li id="${camera.cmra_id}" class="state-${camera.cmra_use_yn}" onclick="detailClick('${camera.cmra_id}')">${camera.drct_lctn}</li>`;
                 detailMap.set(camera.cmra_id, camera);
-            })
+                if (camera.cmra_use_yn === 1) {
+                    cnt++;
+                }
+            });
+            ixrMap.set(obj.ixr_id, obj);
+            intersectionStr += `<ul id="${obj.ixr_id}" onclick="ixrClick('${obj.ixr_id}')">
+                                    <div>${obj.ixr_nm} (${cnt}/${obj.detail.length})</div>
+                                    <ul class="cmra-list">`;
+            intersectionStr += detailStr
             intersectionStr += `</ul></ul>`;
 
 
@@ -140,7 +150,7 @@
 
     if (cctv && cctv.length > 0) {
         cctv.forEach((obj)=>{
-            cctvStr +=`<ul id="${obj.cctv_mngm_nmbr}" onclick="cctvClick('${obj.cctv_mngm_nmbr}')"><div>${obj.istl_lctn_nm}</div></ul>`;
+            cctvStr +=`<ul id="${obj.cctv_mngm_nmbr}" onclick="cctvClick('${obj.cctv_mngm_nmbr}')"><div class="state-${obj.hmpg_dspl_en}">${obj.istl_lctn_nm}</div></ul>`;
             cctvMap.set(obj.cctv_mngm_nmbr, obj);
         });
         $list.html(cctvStr);

+ 8 - 12
src/main/resources/templates/admin/popup-view.html

@@ -31,22 +31,11 @@
                 <div>
                     <input class="title" name="b_subject" th:value="${popup.getTitle()}" readonly>
                     <div id="p_content" class="b_content"></div>
-                    <textarea id="content" class="b_content" style="display: none;" rows="15" th:text="${popup.getUrl()}" readonly></textarea>
+                    <textarea id="content" class="b_content" style="display: none;" rows="15" readonly></textarea>
                     <div class="post-box">
                         <div>공지 기간</div>
                         <input type="text" id="post" class="post_from" name="post_from" disabled="disabled">
                     </div>
-<!--                    <div class="attach-box admin">-->
-<!--                        <div class="attach">-->
-<!--                            <div th:if="${popup.getImgId() == null or #strings.isEmpty(popup.getImgId())}">첨부파일 없음</div>-->
-<!--                            <div class="attach-file" th:if="${popup.getImgName() != null and not #strings.isEmpty(popup.getImgName())}"-->
-<!--                                 th:text="${popup.getImgName()}" th:title="${popup.getImgName() + ' 다운로드'}"-->
-<!--                                 th:onclick="attachFileDownload([[${popup.getImgId()}]], [[${popup.getImgName()}]])"-->
-<!--                            ></div>-->
-<!--                        </div>-->
-<!--                        <input type="file" name="attachFile" id="attach-file" accept="image/*">-->
-<!--                        <div class="bl-button off attach-btn" onclick="attachFile()">파일첨부</div>-->
-<!--                    </div>-->
                 </div>
             </div>
         </div>
@@ -70,6 +59,12 @@
     const popupId     = popup.popupid;
     const popupTitle  = popup.title;
     const $post       = $('#post');
+    $pContent.html(popup.pcontent);
+    let boardAArr = $('#p_content a');
+    if (boardAArr.length > 0) {
+        boardAArr.attr({target : '_blank', rel : 'noreferrer noopener'});
+    }
+
     const post = $post.daterangepicker({
         timePicker: false,
         startDate: new Date(popup.post_from),
@@ -323,6 +318,7 @@
             fOnAppLoad: function () {
                 //기존 저장된 내용의 text 내용을 에디터상에 뿌려주고자 할때 사용
                 let content = popup.pcontent;
+                console.log(content);
                 if (content === null) {
                     content = "";
                 }

+ 37 - 23
src/main/resources/templates/admin/popup-write.html

@@ -9,6 +9,7 @@
     <title>포항시 교통정보센터</title>
     <th:block th:include="/include/head.html"></th:block>
     <th:block th:include="/include/daterangepicker.html"></th:block>
+    <script th:src="@{/js/naverEditor/js/HuskyEZCreator.js}" charset="UTF-8"></script>
     <link rel="stylesheet" th:href="@{/css/notice.css}">
 </head>
 <body id="body">
@@ -26,18 +27,11 @@
             <div class="view-box">
                 <div>
                     <input class="title modify" name="b_subject" placeholder="팝업 제목을 입력해주세요.">
-                    <textarea class="b_content modify" rows="16" name="b_content" placeholder="링크 주소가 있다면 URL을 입력해주세요. ex) https://www.naver.com"></textarea>
+                    <textarea id="content" class="b_content" rows="16" name="b_content"></textarea>
                     <div class="post-box">
                         <div>공지 기간</div>
                         <input type="text" id="post" th:class="modify" name="post">
                     </div>
-                    <div class="attach-box admin">
-                        <div class="attach modify">
-                            <div>첨부파일 없음</div>
-                        </div>
-                        <input type="file" name="attachFile" id="attach-file">
-                        <div class="bl-button attach-btn" onclick="attachFile()">파일첨부</div>
-                    </div>
                 </div>
             </div>
         </div>
@@ -52,6 +46,25 @@
     const $attach     = $('.attach');
     const $attachFile = $('#attach-file');
     const $post       = $('#post');
+    const object      = [];
+    nhn.husky.EZCreator.createInIFrame({
+        oAppRef: object,
+        elPlaceHolder: "content",
+        sSkinURI: "/js/naverEditor/SmartEditor2Skin.html",
+        fCreator: "createSEditor2",
+        htParams: {
+            // 툴바 사용 여부 (true:사용/ false:사용하지 않음)
+            bUseToolbar: true,
+            // 입력창 크기 조절바 사용 여부 (true:사용/ false:사용하지 않음)
+            bUseVerticalResizer: true,
+            // 모드 탭(Editor | HTML | TEXT) 사용 여부 (true:사용/ false:사용하지 않음)
+            bUseModeChanger: true
+        },
+        fOnAppLoad: function () {
+            //기존 저장된 내용의 text 내용을 에디터상에 뿌려주고자 할때 사용
+            object.getById["content"].exec("PASTE_HTML", ['']);
+        }
+    });
 
     const post = $post.daterangepicker({
         timePicker: false,
@@ -71,12 +84,6 @@
         drops: 'up',
     });
 
-    let isMobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent);
-
-    if (isMobile) {
-        $content.attr('rows', 10);
-    }
-
     function attachFile() {
         $attachFile.click();
     }
@@ -95,10 +102,11 @@
     }
 
     function save() {
-        const title   = $title.val();
-        const url     = $content.val();
-        const file    = $attachFile[0].files[0];
-        const postVal = $post.val();
+        object.getById["content"].exec("UPDATE_CONTENTS_FIELD", []);
+        const title    = $title.val();
+        const pcontent = $content.val();
+        // const file    = $attachFile[0].files[0];
+        const postVal  = $post.val();
 
         let postFrom  = "";
         let postTo    = "";
@@ -120,6 +128,11 @@
             return alert("팝업 공지 제목을 입력해주세요.");
         }
 
+        if (isNull(pcontent)) {
+            $content.focus();
+            return alert("팝업 공지 내용을 입력해주세요.");
+        }
+
         if (isNull(postFrom)) {
             $post.focus();
             return alert("팝업 공지 기간 시작일을 입력해주세요.");
@@ -130,15 +143,16 @@
             return alert("팝업 공지 기간 종료일을 입력해주세요.");
         }
 
-        if (!file) {
-            return alert("팝업 이미지 파일을 첨부해주세요.")
-        }
+        // if (!file) {
+        //     return alert("팝업 이미지 파일을 첨부해주세요.")
+        // }
 
         formData.append("title", title);
-        formData.append("url", url);
+        formData.append("url", null);
+        formData.append("pcontent", pcontent);
         formData.append("postForm", postFrom);
         formData.append("postTo", postTo);
-        formData.append("attachFile", file);
+        formData.append("attachFile", null);
 
         $.ajax({
             url: '/api/popup/writePopup',

+ 52 - 1
src/main/resources/templates/main/main.html

@@ -65,9 +65,60 @@
                             <div>교통정보센터 소개 ></div>
                         </div>
                     </div>
+                    <div class="bottom">
+                        <div>홍보 영상</div>
+                        <video></video>
+                    </div>
                 </div>
             </div>
         </div>
         <th:block th:include="/include/footer.html"></th:block>
     </body>
-</html>
+</html>
+
+<script th:inline="javascript">
+    //    let isMobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent);
+    const popup = [[${popup}]];
+    if (popup && popup.length > 0) {
+        let top = $('header').innerHeight();
+        let left = 0;
+        let gap  = 10;
+        popup.forEach((obj, idx)=>{
+            const cookieName = 'p_' + obj.popupid;
+            const cookieInfo = getCookie(cookieName);
+
+            if (!cookieInfo) {
+                setCookie(cookieName, 'Y', null);
+            }
+            if (cookieInfo === 'N') {
+                return;
+            }
+            const popupDiv = $(`<div id="popup_${obj.popupid}" class="popup_content">
+                                    <div id="${obj.popupid}_div">
+                                        ${obj.pcontent}
+                                    </div>
+                                    <div class="close-box">
+                                        <label for="popup_check_${obj.popupid}">오늘 하루 열지 않기</label>
+                                        <input id="popup_check_${obj.popupid}" type="checkbox" value="${obj.popupid}">
+                                        <div onclick="closePopup(${obj.popupid})"> [닫기]</div>
+                                    </div>
+                                </div>`);
+            $('body').append(popupDiv);
+
+            popupDiv.draggable({containment: 'body', handle : '#'+obj.popupid +'_div'});
+
+            let incGap = gap * idx;
+            let incTop = incGap + top;
+            let incLeft = incGap + left;
+            popupDiv.css({
+                top : incTop + 'px',
+                left : incLeft + 'px'
+            })
+        })
+        const popupAlinkArr = $('.popup_content  a');
+        if (popupAlinkArr.length > 0) {
+            popupAlinkArr.attr({target : '_blank', rel : 'noreferrer noopener'});
+        }
+    }
+
+</script>

+ 9 - 6
src/main/resources/templates/notice/view.html

@@ -19,7 +19,8 @@
             <div class="view-box">
                 <div>
                     <input class="title" th:value="${notice.getBSubject()}" readonly>
-                    <textarea class="b_content" rows="19" th:text="${notice.getBContent()}" readonly></textarea>
+                    <div class="b_content"></div>
+                    <!--                    <textarea class="b_content" rows="19" th:text="${notice.getBContent()}" readonly></textarea>-->
                     <div class="attach-box">
                         <div class="attach">
                             <div th:if="${notice.getAttachFile() == '||' or #strings.isEmpty(notice.getAttachFile())}">첨부파일 없음</div>
@@ -40,11 +41,13 @@
 </body>
 </html>
 <script th:inline="javascript">
-    let isMobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent);
-
-    if (isMobile) {
-        $('.b_content').attr('rows', 10);
-    }
+    // let isMobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent);
+    //
+    // if (isMobile) {
+    //     $('.b_content').attr('rows', 10);
+    // }
+    const notice = [[${notice}]];
+    $('.b_content').html(notice.b_content);
 
     function attachFileDownload(index, notice, fileName) {
         let attachFileId = notice.attach_file_id;