notice-view.html 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325
  1. <!DOCTYPE html>
  2. <html lang="en" xmlns:th=http://www.thymeleaf.org>
  3. <head>
  4. <meta charset="utf-8"/>
  5. <meta name="viewport" content="width=device-width,initial-scale=1"/>
  6. <meta name="description" content="포항시 교통정보센터입니다"/>
  7. <meta http-equiv="X-UA-Compatible" content="IE=edge"/>
  8. <title>포항시 교통정보센터</title>
  9. <th:block th:include="include/head.html"></th:block>
  10. <link rel="stylesheet" th:href="@{/css/notice.css}">
  11. <script th:src="@{/js/naverEditor/js/HuskyEZCreator.js}" charset="UTF-8"></script>
  12. </head>
  13. <body id="body">
  14. <th:block th:include="include/admin-header.html"></th:block>
  15. <div class="noticeWrap">
  16. <div class="container view">
  17. <h2 class="admin-header">공지사항</h2>
  18. <div class="content admin-view">
  19. <div class="button-box">
  20. <div class="bl-button" onclick="movePath('/phits/notice-list')">목록</div>
  21. <div style="display: flex;">
  22. <div class="bl-button edit-btn" onclick="edit()">편집</div>
  23. <div class="bl-button off save-btn" onclick="save()">저장</div>
  24. <div class="wt-button del-btn" onclick="delEvent()">삭제</div>
  25. <div class="wt-button off x-btn" onclick="cancel()">취소</div>
  26. </div>
  27. </div>
  28. <div class="view-box">
  29. <div>
  30. <input class="title" name="b_subject" placeholder="제목" th:value="${notice.getBSubject()}" readonly>
  31. <div id="b_content" class="b_content"></div>
  32. <textarea id="content" placeholder="내용" style="display: none;" class="b_content" rows="16" name="b_content" readonly></textarea>
  33. <div class="attach-box admin">
  34. <div class="attach">
  35. <div th:if="${notice.getAttachFile() == '||' or #strings.isEmpty(notice.getAttachFile())}">첨부파일 없음</div>
  36. <div class="attach-file" th:if="${notice.getAttachFile() != '||' and not #strings.isEmpty(notice.getAttachFile())}"
  37. th:each="item, i : ${#strings.arraySplit(notice.getAttachFile(), '|')}"
  38. th:text="${item}" th:title="${item + ' 다운로드'}"
  39. th:onclick="attachFileDownload([[${i.index}]], [[${notice.getBoardNo()}]], [[${notice.getAttachFileId()}]],[[${item}]])"
  40. ></div>
  41. </div>
  42. <input type="file" name="attachFile" id="attach-file">
  43. <div class="bl-button off attach-btn" onclick="attachFile()">파일첨부</div>
  44. </div>
  45. </div>
  46. </div>
  47. </div>
  48. </div>
  49. </div>
  50. <th:block th:include="include/footer.html"></th:block>
  51. <div class="modal loading">
  52. <img class="loading-img" width="150" height="150" src="/images/background/loading.png" alt="로딩 이미지">
  53. </div>
  54. </body>
  55. </html>
  56. <script th:inline="javascript">
  57. const $delete = $('.del-btn');
  58. const $save = $('.save-btn');
  59. const $edit = $('.edit-btn');
  60. const $cancel = $('.x-btn');
  61. const $title = $('.title');
  62. const $content = $('#content');
  63. const $bContent = $('#b_content');
  64. const $attach = $('.attach');
  65. const $attachBtn = $('.attach-btn');
  66. const $attachFile = $('#attach-file');
  67. const notice = [[${notice}]];
  68. let object = [];
  69. $bContent.html(notice.b_content);
  70. let boardAArr = $('.b_content a');
  71. if (boardAArr.length > 0) {
  72. boardAArr.attr({target : '_blank', rel : 'noreferrer noopener'});
  73. }
  74. let isMobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent);
  75. if (isMobile) {
  76. $content.attr('rows', 10);
  77. }
  78. function attachFileDownload(index, id, attachFileId, fileName) {
  79. if (attachFileId && attachFileId.split('|').length > 0) {
  80. attachFileId = attachFileId.split('|')[index];
  81. }
  82. const param = {
  83. boardNo : id,
  84. fileId : attachFileId,
  85. }
  86. getDataAsync('/api/notice/attach', 'POST', param, null, (jsonData)=>{
  87. const attachFile = jsonData.attach_file;
  88. const ext = fileName.substring(fileName.lastIndexOf('.') + 1, fileName.length);
  89. if (attachFile && attachFile.length > 0) {
  90. if (!ext) {
  91. // return alert("파일 확장자명이 잘못 되었습니다.");
  92. return alertError('파일 확장자명이 잘못 되었습니다.', '공지사항', null);
  93. }
  94. const file = b64ToBlob(attachFile);
  95. const link = document.createElement('a');
  96. link.href = file;
  97. link.download = fileName;
  98. link.click();
  99. }
  100. else {
  101. alertError(jsonData.message, '공지사항', null);
  102. // alert(jsonData.message);
  103. }
  104. }, null);
  105. }
  106. const b64ToBlob = (byteData) => {
  107. const byteCharacters = atob(byteData);
  108. const byteNumbers = new Array(byteCharacters.length);
  109. for (let i = 0; i < byteCharacters.length; i++) {
  110. byteNumbers[i] = byteCharacters.charCodeAt(i);
  111. }
  112. const byteArray = new Uint8Array(byteNumbers);
  113. const blob = new Blob([byteArray]);
  114. const blobUrl = window.URL.createObjectURL(blob);
  115. return blobUrl;
  116. };
  117. function attachFile() {
  118. $attachFile.click();
  119. }
  120. $attachFile.on("change", function() {
  121. const changeFile = $(this)[0].files[0];
  122. if (changeFile) {
  123. const fileName = changeFile.name;
  124. $attach.html('<div><span class="attach-delete" title="첨부파일 제거" onclick="deleteAttach()"></span>' + fileName + '</div>');
  125. }
  126. })
  127. function edit() {
  128. $bContent.css({display: 'none'});
  129. $content.css({display : 'block'});
  130. initEditEditor();
  131. $delete.addClass('off');
  132. $edit.addClass('off');
  133. $save.removeClass('off');
  134. $cancel.removeClass('off');
  135. $title.addClass('modify');
  136. $content.addClass('modify');
  137. $attach.addClass('modify');
  138. $attachBtn.removeClass('off');
  139. $title.attr('readonly', false);
  140. $content.attr('readonly', false);
  141. const fileName = $attach.children().eq(0).text();
  142. if (fileName !== '첨부파일 없음') {
  143. $attach.html('<div><span class="attach-delete" title="첨부파일 제거" onclick="deleteAttach()"></span>' + fileName + '</div>')
  144. }
  145. }
  146. function deleteAttach() {
  147. $attachFile.val("");
  148. $attach.html("<div>첨부파일 없음</div>");
  149. }
  150. function delEvent() {
  151. const boardNo = [[${notice.getBoardNo()}]];
  152. const bSubject = [[${notice.getBSubject()}]];
  153. const message = '번호 : ' + boardNo + '<br>제목 : ' + bSubject + '<br>게시물을 삭제하시겠습니까?';
  154. alertMessage('red', message, '공지사항', null, ()=>{
  155. getDataAsync('/api/notice/deleteNotice', 'DELETE', {boardNo : boardNo}, null, (jsonData)=>{
  156. if (jsonData) {
  157. alertMessage('blue', jsonData.message, '공지사항', null, ()=>{
  158. if (jsonData.success === "S") {
  159. window.location.href = '/phits/notice-list';
  160. }
  161. }, true)
  162. }
  163. }, null);
  164. });
  165. }
  166. function save() {
  167. object.getById["content"].exec("UPDATE_CONTENTS_FIELD", []);
  168. const file = $attachFile[0].files[0];
  169. const title = $title.val();
  170. const attachNm = notice.attach_file.split("|")[0];
  171. let content = $content.val();
  172. if (content && content.length >= 13) {
  173. let trimContent = content.replaceAll("<p>&nbsp;</p>", "");
  174. if (trimContent === "") {
  175. content = "";
  176. }
  177. else {
  178. let spaceGap = content.length - 13;
  179. let spaceEl = content.substring(spaceGap);
  180. if (spaceEl === '<p>&nbsp;</p>') {
  181. content = content.substring(0, spaceGap);
  182. }
  183. }
  184. }
  185. if (notice.b_subject === title && notice.b_content === content) {
  186. if (file) {
  187. if (file.name === attachNm) {
  188. return alertError('수정하신 내용이 없습니다. 내용을 확인해주세요.', '공지사항');
  189. }
  190. }
  191. else {
  192. const attachFileName = $attach.children().eq(0).text();
  193. if ( (attachFileName === "첨부파일 없음" && attachNm === "") || attachFileName === attachNm) {
  194. return alertError('수정하신 내용이 없습니다. 내용을 확인해주세요.', '공지사항');
  195. }
  196. }
  197. }
  198. if (isNull(title)) {
  199. return alertError('공지사항 제목을 입력해주세요', '공지사항', $title);
  200. }
  201. if (isNull(content)) {
  202. return alertError('공지사항 내용을 입력해주세요', '공지사항', $content);
  203. }
  204. const formData = new FormData();
  205. formData.append("boardNo", notice.board_no);
  206. formData.append("bSubject", title);
  207. formData.append("bContent", content);
  208. if (file && file.name) {
  209. formData.append("attachFileNames", file.name + "|" + file.name + "|" + file.name);
  210. formData.append("attachFile", file);
  211. formData.append("attachFile", file);
  212. formData.append("attachFile", file);
  213. }
  214. else {
  215. formData.append("attachFile", null);
  216. formData.append("attachFileNames", "||");
  217. }
  218. $.ajax({
  219. url: '/api/notice/modifyNotice',
  220. processData : false,
  221. contentType: false,
  222. data: formData,
  223. type: 'POST',
  224. success: function(jsonData) {
  225. if (jsonData) {
  226. alertMessage('blue', jsonData.message, '공지사항', null, ()=>{
  227. if (jsonData.success === "S") {
  228. window.location.href = "/phits/notice-view/" + notice.board_no;
  229. }
  230. }, true);
  231. }
  232. },
  233. error: function(error) {
  234. sendErrorMsg(error);
  235. }
  236. });
  237. }
  238. function cancel() {
  239. $content.css('display', 'none');
  240. $bContent.css('display', 'block');
  241. $('iframe').remove();
  242. $delete.removeClass('off');
  243. $edit.removeClass('off');
  244. $save.addClass('off');
  245. $cancel.addClass('off');
  246. $title.removeClass('modify');
  247. $content.removeClass('modify');
  248. $attach.removeClass('modify');
  249. $attachBtn.addClass('off');
  250. $title.attr('readonly', true);
  251. $content.attr('readonly', true);
  252. const attachFileNames = notice.attach_file;
  253. if (attachFileNames) {
  254. const attachFileName = attachFileNames.split("|")[0];
  255. let attachText = "<div>첨부파일 없음</div>";
  256. if (attachFileName) {
  257. attachText = `<div class="attach-file" title="${attachFileName} 다운로드"
  258. onclick="attachFileDownload(0, ${notice.board_no}, '${notice.attach_file_id}','${attachFileName}')">
  259. ${attachFileName}
  260. </div>`;
  261. }
  262. $attach.html(attachText);
  263. }
  264. }
  265. function initEditEditor() {
  266. object = [];
  267. nhn.husky.EZCreator.createInIFrame({
  268. oAppRef: object,
  269. elPlaceHolder: "content",
  270. sSkinURI: "/js/naverEditor/SmartEditor2Skin.html",
  271. fCreator: "createSEditor2",
  272. htParams: {
  273. // 툴바 사용 여부 (true:사용/ false:사용하지 않음)
  274. bUseToolbar: true,
  275. // 입력창 크기 조절바 사용 여부 (true:사용/ false:사용하지 않음)
  276. bUseVerticalResizer: true,
  277. // 모드 탭(Editor | HTML | TEXT) 사용 여부 (true:사용/ false:사용하지 않음)
  278. bUseModeChanger: true
  279. },
  280. fOnAppLoad: function () {
  281. //기존 저장된 내용의 text 내용을 에디터상에 뿌려주고자 할때 사용
  282. object.getById["content"].exec("PASTE_HTML", [notice.b_content]);
  283. }
  284. });
  285. //SE2M_Configuration.js
  286. //nhn.husky.SE2M_Configuration.SE2M_Hyperlink -> bAutolink : true // 자동링크 생성 여부
  287. }
  288. $(window).ajaxStart(function () {
  289. $('.modal.loading').css('display', 'flex');
  290. })
  291. .ajaxStop(function () {
  292. $('.modal.loading').css('display', 'none');
  293. });
  294. </script>