admin.js 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607
  1. $(function ()
  2. {
  3. /*$('.datepicker').datetimepicker({
  4. dayViewHeaderFormat: 'YYYY MMMM',
  5. format: 'YYYY-MM-DD',
  6. useCurrent: false,
  7. ignoreReadonly: true,
  8. // defaultDate: new Date(),
  9. allowInputToggle: true
  10. });*/
  11. $(".date-picker").datepicker({
  12. dayViewHeaderFormat: 'YYYY MMMM',
  13. format: 'YYYY-MM-DD',
  14. useCurrent: false,
  15. dateFormat: 'yy-mm-dd', // 날짜 포맷이다. 보통 yy-mm-dd 를 많이 사용하는것 같다.
  16. prevText: '이전 달', // 마우스 오버시 이전달 텍스트
  17. nextText: '다음 달', // 마우스 오버시 다음달 텍스트
  18. closeText: '닫기', // 닫기 버튼 텍스트 변경
  19. currentText: '오늘', // 오늘 텍스트 변경
  20. monthNames: ['1월', '2월', '3월', '4월', '5월', '6월', '7월', '8월', '9월', '10월', '11월', '12월'], //한글 캘린더중 월 표시를 위한 부분
  21. monthNamesShort: ['1월', '2월', '3월', '4월', '5월', '6월', '7월', '8월', '9월', '10월', '11월', '12월'], //한글 캘린더 중 월 표시를 위한 부분
  22. dayNames: ['일', '월', '화', '수', '목', '금', '토'], //한글 캘린더 요일 표시 부분
  23. dayNamesShort: ['일', '월', '화', '수', '목', '금', '토'], //한글 요일 표시 부분
  24. dayNamesMin: ['일', '월', '화', '수', '목', '금', '토'], // 한글 요일 표시 부분
  25. showMonthAfterYear: true, // true : 년 월 false : 월 년 순으로 보여줌
  26. yearSuffix: '년', //
  27. showButtonPanel: true, // 오늘로 가는 버튼과 달력 닫기 버튼 보기 옵션
  28. // showOn: "button", // 이미지로 사용 , both : 엘리먼트와 이미지 동시사용
  29. // buttonImage: "/admin/common/images/btn_cal.gif", // 이미지 주소
  30. // buttonImageOnly: true // 이미지만 보이기
  31. onClose: function(value) {
  32. let date = getOnlyDigit(value);
  33. if(date) {
  34. this.value = moment(date, "YYYY-MM-DD").format("YYYY-MM-DD");
  35. }else{
  36. this.value = "";
  37. }
  38. }
  39. });
  40. // 이미지 없으면 기본값으로
  41. function imgError() {
  42. event.srcElement.src = BASE_URL + "/images/default/no_image.png";
  43. event.srcElement.style.width = "60";
  44. event.srcElement.style.height = "60";
  45. }
  46. let allImage = document.getElementsByTagName("img");
  47. if (allImage.length > 0) {
  48. for (let i = 0; i < allImage.length; i++) {
  49. allImage[i].onerror = imgError;
  50. }
  51. }
  52. if(typeof select2 != "undefined" && document.querySelectorAll(".select2").length > 0) {
  53. $('.select2').select2();
  54. }
  55. });
  56. window.addEventListener("load", function() {
  57. let tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'));
  58. let tooltipList = tooltipTriggerList.map(function (e) {
  59. return new bootstrap.Tooltip(e)
  60. });
  61. let popoverTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="popover"]'));
  62. let popoverList = popoverTriggerList.map(function (e) {
  63. return new bootstrap.Popover(e)
  64. });
  65. });
  66. $.validator.setDefaults({ // Bootstrap Required.
  67. ignore: [], // hidden
  68. debug: false,
  69. onfocusout: function () {
  70. for (let e of this.elements()) {
  71. $(e).valid();
  72. }
  73. },
  74. invalidHandler: function (event, validator) {
  75. return onlyInputUnComma(event.currentTarget);
  76. },
  77. submitHandler: function (form) {
  78. return onlyInputUnComma(form);
  79. },
  80. showErrors: function (errorMap, errorList) {
  81. for (let e of this.elements()) {
  82. if (e.ariaInvalid === "false") {
  83. $(e).tooltip('dispose');
  84. }
  85. }
  86. this.successList.forEach(function (e) {
  87. let field = $(e);
  88. if (typeof field.tooltip !== "undefined") { // 3.3.7은 destroy 4.0 이후 dispose
  89. field.tooltip('dispose');
  90. }
  91. });
  92. errorList.forEach(function (field) {
  93. $(field.element).tooltip('dispose').tooltip({
  94. placement: "bottom",
  95. trigger: "manual",
  96. title: field.message
  97. }).tooltip('show');
  98. });
  99. }
  100. });
  101. // 목록 버튼상자
  102. function formSubmit(f, actType, actPage, actDesc)
  103. {
  104. if ($("input:checkbox.list-check-box:checked", f).length < 1) {
  105. alert('자료를 하나 이상 선택하세요.');
  106. return false;
  107. }
  108. let msg;
  109. let input = document.createElement('input');
  110. input.setAttribute('type', 'hidden');
  111. input.setAttribute('name', '_method');
  112. switch(actType) {
  113. case 'update' :
  114. msg = '선택한 자료를 정말 수정 하시겠습니까?';
  115. input.setAttribute('value', 'PUT');
  116. break;
  117. case 'save' :
  118. msg = '선택한 자료를 정말 저장 하시겠습니까?';
  119. break;
  120. case 'delete' :
  121. msg = '선택한 자료를 정말 삭제 하시겠습니까?';
  122. input.setAttribute('value', 'DELETE');
  123. break;
  124. case 'recover' :
  125. msg = '선택한 자료를 정말 복원 하시겠습니까?';
  126. break;
  127. case 'trash' :
  128. msg = '선택한 자료를 정말 휴지통으로 이동하시겠습니까?';
  129. break;
  130. case 'action' :
  131. msg = '선택한 자료를 정말 ' + actDesc + ' 처리 하시겠습니까?';
  132. break;
  133. }
  134. if(!confirm(msg)) {
  135. return false;
  136. }
  137. f.appendChild(input);
  138. f.action = actPage;
  139. f.submit();
  140. }
  141. // 선택삭제
  142. function deleteCheck()
  143. {
  144. if (confirm('정말 삭제하시겠습니까? 삭제하신 후에는 복구가 불가능합니다.')) {
  145. return true;
  146. } else {
  147. return false;
  148. }
  149. }
  150. // 모든 라디오, 체크박스 선택/해제
  151. function allCheckThis(obj) {
  152. let that = (obj || null);
  153. if (that) {
  154. if (that.name) {
  155. let checkBoxs = document.getElementsByName(that.name + '[]');
  156. checkBoxs.forEach(function (item, index) {
  157. if (item.checked) {
  158. item.checked = false;
  159. } else {
  160. item.checked = true;
  161. }
  162. });
  163. } else { // name이 설정되지 않으면 근처 input 검색하여 처리
  164. let checkBoxs = $(that).parent().parent().find('input');
  165. checkBoxs.each(function () {
  166. if (this.value !== '') {
  167. if (this.checked) {
  168. this.checked = false;
  169. } else {
  170. this.checked = true;
  171. }
  172. }
  173. });
  174. }
  175. }
  176. }
  177. // 팝업창 생성
  178. // http://naiyumie.tistory.com/entry/windowopen-windowshowModalDialog-%EC%98%B5%EC%85%98-%EC%9C%88%EB%8F%84%EC%9A%B0-%EC%98%A4%ED%94%88 사용법 참조
  179. function ShowModalWindow(url, winName, width, height, reload) {
  180. var reload = (reload || true);
  181. /*
  182. var ua = window.navigator.userAgent;
  183. if(ua.indexOf('MSIE') > 0 || ua.indexOf('Trident') > 0){
  184. aWindow = window.showModalDialog(url, self, 'dialogWidth='+width+'px;dialogHeight='+height+'px;scroll:yes;');
  185. if(!reload){
  186. if(typeof aWindow != "undefined" && aWindow) {
  187. document.location.reload();
  188. }
  189. }
  190. }else{
  191. aWindow = window.open(url, winName, 'menubar=no,status=no,toolbar=no,resizable=no,width='+width+',height='+height+',titlebar=no,scrollbars=no,alwaysRaised=yes,directories=no');
  192. }
  193. */
  194. var aWindow = window.open(url, winName, 'menubar=no,status=no,toolbar=no,resizable=no,width=' + width + ',height=' + height + ',titlebar=no,scrollbars=no,alwaysRaised=yes,directories=no');
  195. function bodyLoaded() {
  196. if ((aWindow && aWindow.document && aWindow.document.body)) { // Loaded
  197. aWindow.focus();
  198. } else if (aWindow && !aWindow.closed) { // looks like we have a pop up blocker
  199. aWindow.location.reload(true);
  200. aWindow.focus();
  201. } else {
  202. window.setTimeout(function () {
  203. bodyLoaded();
  204. }, 100);
  205. }
  206. }
  207. bodyLoaded();
  208. return aWindow;
  209. }
  210. // 팝업 크기 자동조절
  211. function popupAutoResize() {
  212. let strWidth;
  213. let strHeight;
  214. // innerWidth / innerHeight / outerWidth / outerHeight 지원 브라우저
  215. if (window.innerWidth && window.innerHeight && window.outerWidth && window.outerHeight) {
  216. strWidth = parseInt(document.body.children[1].scrollWidth) + (window.outerWidth - window.innerWidth);
  217. strHeight = parseInt(document.body.children[1].scrollHeight) + (window.outerHeight - window.innerHeight);
  218. } else {
  219. let strDocumentWidth = $(document).outerWidth();
  220. let strDocumentHeight = $(document).outerHeight();
  221. window.resizeTo(strDocumentWidth, strDocumentHeight);
  222. let strMenuWidth = strDocumentWidth - $(window).width();
  223. let strMenuHeight = strDocumentHeight - $(window).height();
  224. strWidth = parseInt(document.body.children[1].scrollWidth) + strMenuWidth;
  225. strHeight = parseInt(document.body.children[1].scrollHeight) + strMenuHeight;
  226. }
  227. window.resizeTo(strWidth, strHeight);
  228. }
  229. /*
  230. // jquery UI 모달에 페이지 호출
  231. function initializeDialog(url)
  232. {
  233. var div = document.createElement("div");
  234. var dialog = $(div).load(url).dialog({
  235. autoOpen: false,
  236. autoResize: true,
  237. width: "90%",
  238. height: "inherit",
  239. position: { my: "center", at: "center", of: window },
  240. resizable: false,
  241. overlay: "background-color: #000;opcacity: 0.8;",
  242. draggable: false,
  243. title: "",
  244. hide: { effect: "none", duration: 150 },
  245. show: { effect: "none", duration: 150 },
  246. model: true,
  247. show: "show",
  248. hide: "hide",
  249. closeOnEscape: true,
  250. buttons: {
  251. '닫기': function(){
  252. dialog.dialog("close");
  253. }
  254. },
  255. close: function(){
  256. form[0].reset();
  257. allFields.removeClass("ui-state-error");
  258. }
  259. });
  260. return dialog.dialog("open");
  261. }
  262. */
  263. // 모달 열기
  264. function initializeModal(element, url) {
  265. if (typeof element == "undefined" || typeof url == "undefined") {
  266. return false;
  267. }
  268. let modalPopup = $(element);
  269. modalPopup.load(url, function (response, status, xhr) {
  270. let msg = null;
  271. switch (status) {
  272. case 'error' :
  273. msg = '페이지를 호출 할 수 없습니다.';
  274. break;
  275. case 'timeout' :
  276. msg = '호출 시간이 초과 되었습니다.';
  277. break;
  278. case 'notmodified' :
  279. msg = 'NOT MODIFIED';
  280. break;
  281. case 'parsererror' :
  282. msg = 'PARSE ERROR';
  283. break;
  284. case 'success' :
  285. modalPopup.modal('show');
  286. break;
  287. }
  288. if (msg) {
  289. alert(msg);
  290. return false;
  291. }
  292. });
  293. }
  294. // 검색기간 지정
  295. function setDateRange(type, so, eo)
  296. {
  297. let w = '';
  298. let m = '';
  299. let y = '';
  300. let d = new Date();
  301. let c = new Date(new Date(new Date().toLocaleDateString()).getTime()+24*60*60*1000-1);
  302. let endDate = $.datepicker.formatDate('yy-mm-dd', d);
  303. $((typeof eo !== "undefined" ? eo : '.s-end-date')).val(endDate);
  304. switch(type) {
  305. case '1d' :
  306. d.setDate(d.getDate() - 1);
  307. break;
  308. case '7d' :
  309. w = d.getDate();
  310. d.setDate(w - 7);
  311. break;
  312. case '15d' :
  313. w = d.getDate();
  314. d.setDate(w - 15);
  315. break;
  316. case '1m' :
  317. m = d.getMonth();
  318. d.setMonth(m - 1);
  319. break;
  320. case '2m' :
  321. m = d.getMonth();
  322. d.setMonth(m - 2);
  323. break;
  324. case '3m' :
  325. m = d.getMonth();
  326. d.setMonth(m - 3);
  327. break;
  328. case '6m' :
  329. m = d.getMonth();
  330. d.setMonth(m - 6);
  331. break;
  332. default :
  333. // y = d.getFullYear();
  334. // d.setFullYear(y - 1);
  335. break;
  336. }
  337. let startDate = d.toISOString().split('T')[0];
  338. $((typeof so !== "undefined" ? so : '.s-start-date')).val(startDate);
  339. }
  340. function byteCheck(txt, byte) {
  341. txt = document.getElementById(txt);
  342. byte = document.getElementById(byte);
  343. let i = 0;
  344. let cnt = 0;
  345. let exceed = 0;
  346. let ch = '';
  347. let maxByte = 2000;
  348. for (i = 0; i < txt.value.length; i++) {
  349. ch = txt.value.charAt(i);
  350. if (escape(ch).length > 4) {
  351. cnt += 2;
  352. } else {
  353. cnt += 1;
  354. }
  355. }
  356. byte.innerHTML = cnt;
  357. if (cnt > maxByte) {
  358. exceed = cnt - maxByte;
  359. alert('메시지 내용은 80바이트를 넘을수 없습니다.\n\n작성하신 메세지 내용은 ' + exceed + 'byte 가 초과되었습니다.\n\n초과된 부분은 자동으로 삭제됩니다.');
  360. let tcnt = 0;
  361. let xcnt = 0;
  362. let tmp = txt.value;
  363. for (i = 0; i < tmp.length; i++) {
  364. ch = tmp.charAt(i);
  365. if (escape(ch).length > 4) {
  366. tcnt += 2;
  367. } else {
  368. tcnt += 1;
  369. }
  370. if (tcnt > 80) {
  371. tmp = tmp.substring(0, i);
  372. break;
  373. } else {
  374. xcnt = tcnt;
  375. }
  376. }
  377. txt.value = tmp;
  378. byte.innerHTML = xcnt;
  379. return true;
  380. }
  381. }
  382. $(document).on('click', '.btn-list-update', function () { // 선택수정
  383. formSubmit(document.f_admin_list, 'update', this.dataset.listUpdateUrl);
  384. });
  385. $(document).on('click', '.btn-list-save', function () { // 선택저장
  386. formSubmit(document.f_admin_list, 'save', this.dataset.listSaveUrl);
  387. });
  388. $(document).on('click', '.btn-list-delete', function () { // 선택삭제
  389. formSubmit(document.f_admin_list, 'delete', this.dataset.listDeleteUrl);
  390. });
  391. $(document).on('click', '.btn-list-trash', function () { // 전체삭제
  392. formSubmit(document.f_admin_list, 'trash', this.dataset.listTrashUrl);
  393. });
  394. $(document).on('click', '.btn-list-recover', function () { // 전체복구
  395. formSubmit(document.f_admin_list, 'recover', this.dataset.listRecoverUrl);
  396. });
  397. $(document).on('click', '.btn-list-action', function () { // 전체복구
  398. formSubmit(document.f_admin_list, 'action', this.dataset.listActionUrl, this.dataset.desc);
  399. });
  400. $(document).on('click', '.btn-write-cancel', function () { // 취소하기
  401. location.href = this.value;
  402. });
  403. $(document).on('click', '.btn-popup-cancel', function () { // 팝업 닫기
  404. window.close();
  405. });
  406. $(document).on('click', '.btn-one-delete', function () {
  407. if (confirm("한번 삭제한 자료는 복구할 방법이 없습니다.\n\n정말 삭제하시겠습니까?")) {
  408. document.location.href = $(this).attr('data-one-delete-url');
  409. return true;
  410. } else {
  411. return false;
  412. }
  413. });
  414. $(document).on('click', '.btn-list-truncate', function () {
  415. if (confirm("휴지통 전체를 비웁니다.\n\n비운 자료는 절대 복구가 불가능합니다. \n\n그래도 진행하시겠습니까?")) {
  416. document.location.href = $(this).attr('data-list-truncate-url');
  417. return true;
  418. } else {
  419. return false;
  420. }
  421. });
  422. $(document).on('click', '.btn-one-recover', function () {
  423. if (confirm('선택한 자료를 정말 복원하시겠습니까?')) {
  424. document.location.href = $(this).attr('data-one-recover-url');
  425. return true;
  426. } else {
  427. return false;
  428. }
  429. });
  430. $(document).on('click', '.btn-one-trash', function () {
  431. if (confirm('선택한 자료를 정말 휴지통으로 이동하시겠습니까?')) {
  432. document.location.href = $(this).attr('data-one-trash-url');
  433. return true;
  434. } else {
  435. return false;
  436. }
  437. });
  438. // 목록에 행 선택시 선택관련 버튼 활성화
  439. $(document).on('click', '.list-check-box, #chkAll', function (e) {
  440. setTimeout(function () {
  441. let $checkedCheckboxes = $('.list-check-box:checked');
  442. let $btnListUpdate = $('.btn-list-update');
  443. let $btnListSelected = $('.btn-list-selected');
  444. if ($checkedCheckboxes.length > 0) {
  445. $btnListUpdate.removeClass('disabled');
  446. $btnListSelected.removeClass('disabled');
  447. } else {
  448. $btnListUpdate.addClass('disabled');
  449. $btnListSelected.addClass('disabled');
  450. }
  451. }, 300);
  452. });
  453. // 목록 전체선택
  454. $(document).on('click', '#chkAll', function (e) {
  455. let chk = document.getElementsByClassName('list-check-box');
  456. for (let i = 0; i < chk.length; i++) {
  457. if (e.target.getAttribute('form') == chk[i].getAttribute('form')) {
  458. chk[i].checked = this.checked;
  459. }
  460. }
  461. if (this.checked) {
  462. $('[data-action]').prop('disabled', false);
  463. } else {
  464. $('[data-action]').prop('disabled', true);
  465. }
  466. });
  467. // 부트스트랩 모달 열기
  468. $(document).on("change click", ".modal-popup", function (e) {
  469. if (typeof (this.dataset.href) != undefined && this.dataset.href != '') {
  470. let index = $('.modal-popup').index(this);
  471. let href = this.dataset.href;
  472. if (params('index', href) != 1) {
  473. if (href.indexOf('?') != -1) {
  474. // this.dataset.href = href + '&index=' + index;
  475. } else {
  476. this.dataset.href = href + '/?index=' + index;
  477. }
  478. }
  479. initializeModal('#modal-popup', this.dataset.href);
  480. }
  481. });
  482. // 체크박스 하나만 선택하기
  483. $(document).on('change', '.only-one-checked', function (e) {
  484. if (this.checked) {
  485. let obj = document.getElementsByName(e.target.name);
  486. for (let i = 0; i < obj.length; i++) {
  487. if (obj[i].value !== e.target.value) {
  488. obj[i].checked = false;
  489. }
  490. }
  491. }
  492. });
  493. // 데이터 테이블
  494. if ($.isFunction($.fn.dataTable)) {
  495. $.extend(true, $.fn.dataTable.defaults, {
  496. columnDefs: [{
  497. targets: 'actions',
  498. className: 'actions',
  499. searchable: false,
  500. sortable: false
  501. }],
  502. lengthMenu: [5, 10, 25, 50, 100, 250, 500],
  503. pageLength: 25,
  504. language: {
  505. search: '',
  506. searchPlaceholder: 'Search'
  507. },
  508. processing: true,
  509. stateSave: true,
  510. stateDuration: 0,
  511. responsive: true,
  512. stateSaveParams: function (settings, data) {
  513. data.search.search = "";
  514. data.start = 0;
  515. },
  516. stateSaveCallback: function (settings, data) {
  517. localStorage.setItem($(this).attr('id'), JSON.stringify(data));
  518. },
  519. stateLoadCallback: function () {
  520. return JSON.parse(localStorage.getItem($(this).attr('id')));
  521. },
  522. initComplete: function (settings, json) {
  523. let self = this.api();
  524. let filter_input = $('#' + settings.nTable.id + '_filter input').unbind();
  525. let search_button = $('<button type="button" class="btn btn-link btn-sm btn-icon ms-2 mb-1" data-bs-toggle="tooltip" title="Search"><i class="fa fa-fw fa-search"></i></button>').click(function () {
  526. self.search(filter_input.val()).draw();
  527. });
  528. let reset_button = $('<button type="button" class="btn btn-link btn-sm btn-icon mb-1" data-bs-toggle="tooltip" title="Reset"><i class="fa fa-fw fa-undo"></i></button>').click(function () {
  529. filter_input.val('');
  530. search_button.click();
  531. });
  532. $(document).keypress(function (event) {
  533. if (event.which === 13) {
  534. search_button.click();
  535. }
  536. });
  537. $('#' + settings.nTable.id + '_filter').append(search_button, reset_button);
  538. }
  539. });
  540. }
  541. // 체크박스 클릭 시 해당 컨텐츠 내용을 숨김/표시 처리
  542. $(document).on("change", ".content-collapse", function (e) {
  543. let target = e.target.dataset.target;
  544. let content = $("#" + target);
  545. if (this.checked) {
  546. content.show();
  547. } else {
  548. content.hide();
  549. }
  550. });
  551. // 맨 위로
  552. $(document).on("click", "#btnScrollTop", function() {
  553. $("#main").scrollTop(0);
  554. });