nodeExcelPopup.jsp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708
  1. <%@ page language="java" contentType="text/html; charset=UTF-8"
  2. pageEncoding="UTF-8"%>
  3. <%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %>
  4. <!DOCTYPE html>
  5. <html>
  6. <head>
  7. <meta charset="UTF-8">
  8. <title>노드 일괄등록</title>
  9. <script type="text/javascript" src="/js/ext/jquery.min.js"></script>
  10. <script type="text/javascript" src="/js/ext/xlsx.full.min.js"></script>
  11. <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css">
  12. <style>
  13. body {
  14. background-color: rgb(52,55,58);
  15. padding: 15px;
  16. margin: 0;
  17. color: #cccccc;
  18. font-size: 18px;
  19. font-weight: bold;
  20. display: grid;
  21. grid-template-rows: auto;
  22. grid-row-gap: 15px;
  23. }
  24. .batch-popup {
  25. font-weight: bold;
  26. display: grid;
  27. /* grid-template-rows: 60px 30px 210px 1fr; */
  28. grid-template-rows: 60px 30px 1fr;
  29. grid-row-gap: 30px;
  30. padding: 15px;
  31. background: #777;
  32. }
  33. .input-file {
  34. display: grid;
  35. grid-template-columns: min-content auto;
  36. border: 2px solid #706f6f;
  37. background: #ccc;
  38. height: 60px;
  39. }
  40. .input-file .file-label {
  41. display: grid;
  42. width: 115px;
  43. height: 60px;
  44. line-height: 20px;
  45. padding: 0 10px;
  46. border-radius: 2px;
  47. background-color: #eee;
  48. color: #706f6f;
  49. cursor: pointer;
  50. border-right: 2px solid #706f6f;
  51. font-size: 14px;
  52. align-items: center;
  53. }
  54. .input-file .upload-file {
  55. color: #777;
  56. padding: 5px;
  57. font-size: 14px;
  58. display: grid;
  59. grid-template-rows: 1fr 1fr;
  60. }
  61. .input-file .upload-file .upload-button {
  62. display: grid;
  63. grid-template-columns: 8fr 1fr;
  64. }
  65. .input-file .upload-file .upload-button .validate {
  66. background: #efefef;
  67. border: 1px solid #767676;
  68. text-align: center;
  69. line-height: 23px;
  70. font-size: 13px;
  71. font-weight: 400;
  72. color: rgb(0 0 0);
  73. border-radius: 3px;
  74. }
  75. .input-file .upload-file .upload-button .validate:hover {
  76. cursor: pointer;
  77. }
  78. .input-option {
  79. display: grid;
  80. grid-template-columns: min-content auto;
  81. border: 2px solid #706f6f;
  82. background: #ccc;
  83. height: 30px;
  84. }
  85. .input-option .option-label {
  86. display: grid;
  87. width: 115px;
  88. height: 30px;
  89. padding: 0 10px;
  90. border-radius: 2px;
  91. background-color: #eee;
  92. color: #706f6f;
  93. cursor: pointer;
  94. border-right: 2px solid #706f6f;
  95. font-size: 14px;
  96. align-items: center;
  97. }
  98. .input-option .upload-option {
  99. color: #777;
  100. padding: 5px;
  101. font-size: 14px;
  102. display: grid;
  103. grid-template-columns: 1fr 1fr;
  104. }
  105. .button-div {
  106. /* position: relative; */
  107. /* top: 45px; */
  108. height: 55px;
  109. /* line-height: 58px; */
  110. border-top: 2px solid rgb(170 170 183);
  111. display: grid;
  112. grid-template-columns: 92px 92px;
  113. justify-content: space-between;
  114. align-items: end;
  115. }
  116. .confirm {
  117. width: 80px;
  118. color:#706f6f;
  119. height: 36px;
  120. line-height: 40px;
  121. border: 1px solid #eeeeee;
  122. background: #f7f7f6;;
  123. text-align: left;
  124. padding-left: 10px;
  125. }
  126. .cancel {
  127. width: 80px;
  128. color:#706f6f;
  129. height: 36px;
  130. line-height: 40px;
  131. border: 1px solid #eeeeee;
  132. background: #f7f7f6;;
  133. text-align: left;
  134. padding-left: 10px;
  135. }
  136. .confirm:hover,
  137. .cancel:hover {
  138. cursor: pointer;
  139. background: rgb(0, 120, 215);
  140. color: white;
  141. border-color: white;
  142. }
  143. .inspection-option {
  144. /* display: grid; */
  145. display: none;
  146. grid-template-rows: auto;
  147. grid-row-gap: 10px;
  148. }
  149. .inspection-label {
  150. color: #ffffff;
  151. padding: 0 3px;
  152. height: 30px;
  153. }
  154. .inspection-result {
  155. border: 2px solid #706f6f;
  156. background: #ccc;
  157. height: 166px;
  158. color: #000;
  159. overflow: auto;
  160. font-size: 13px;
  161. font-weight: 400;
  162. }
  163. </style>
  164. </head>
  165. <body>
  166. 노드정보 일괄등록
  167. <div class="batch-popup">
  168. <div class="input-file">
  169. <label for="upload01" class="file-label">엑셀 업로드<br>(파일)</label>
  170. <div class="upload-file">
  171. *등록하려는 엑셀 파일입니다.
  172. <div class="upload-button">
  173. <input type="file" id="upload01" name="upload01" onchange="regionExcel()">
  174. <div class="validate">
  175. 검사
  176. </div>
  177. </div>
  178. </div>
  179. </div>
  180. <div class="input-option">
  181. <div class="option-label">업로드 옵션</div>
  182. <div class="upload-option">
  183. <div>
  184. <input type="radio" id="option1" name="option" value="upload1" checked>
  185. <label for="option1">중복 데이터 제외 후 추가</label>
  186. </div>
  187. <div>
  188. <input type="radio" id="option2" name="option" value="upload2">
  189. <label for="option2">중복 데이터 수정 및 추가</label>
  190. </div>
  191. </div>
  192. </div>
  193. <div class="inspection-option">
  194. <div class="inspection-label">검사 결과</div>
  195. <div class="inspection-result">
  196. </div>
  197. </div>
  198. <div class="button-div">
  199. <div class="confirm">
  200. <i class='fa fa-check' style="font-size: 25px;" aria-hidden='true'></i> 등록
  201. </div>
  202. <div class="cancel">
  203. <i class='fa fa-times' style="font-size: 25px;" aria-hidden='true'></i> 취소
  204. </div>
  205. </div>
  206. </div>
  207. <script>
  208. let nodeIdData = [];
  209. allNodeId();
  210. // 검사 버튼 이벤트
  211. $(".validate").on('click',()=>{
  212. if(!addData.length){
  213. alert("엑셀이 등록되지 않았거나, 엑셀 데이터가 존재하지 않습니다.")
  214. } else {
  215. $(".batch-popup")[0].style["grid-template-rows"] = "60px 30px 210px 1fr"
  216. $(".inspection-option")[0].style["display"] = "grid"
  217. $(window)[0].resizeTo(616, 620);
  218. validateExcel($("input[name='option']:checked").val());
  219. }
  220. })
  221. // 확인 버튼 이벤트
  222. $(".confirm").on('click',()=>{
  223. saveExcel();
  224. })
  225. // 취소 버튼 이벤트
  226. $(".cancel").on('click',()=>{
  227. window.close();
  228. })
  229. let obj;
  230. let region = [];
  231. // 엑셀 READ result
  232. let addData = [];
  233. // 예외처리 후 result
  234. let result = [];
  235. // 예외처리 에러리스트
  236. let errList = [];
  237. // 에러 메세지
  238. let message = "";
  239. // 검사 여부
  240. let validateYN = 0;
  241. // 라디오 버튼
  242. $("input[name='option']:radio").change(function () {
  243. validateYN = 0;
  244. });
  245. // 삭제 항목
  246. let delStr = ""
  247. // 형식 검사를 위한 정규식
  248. const latitudePattern = /^([0-9]{1,2})([.]{1})([0-9]{1,14})$/
  249. const longitudePattern = /^([0-9]{1,3})([.]{1})([0-9]{1,14})$/
  250. const nodeIdPattern = /^[0-9]{1,10}$/;
  251. const ntPattern = /^[0-9]*$/;
  252. function regionArr(arr){
  253. $.ajax({
  254. url:"/api/getAllRegion.do",
  255. async:false,
  256. type: "get",
  257. dataType: "json",
  258. success:function(result){
  259. arr.push(result);
  260. }, error: function(error) {
  261. console.log(error)
  262. }
  263. })
  264. }
  265. // 지역 코드 정의
  266. let regionIndex = {
  267. "서울시" : "L01",
  268. "인천시" : "L02",
  269. "부천시" : "L03",
  270. "광명시" : "L04",
  271. "안양시" : "L05",
  272. "과천시" : "L06",
  273. "안산시" : "L07",
  274. "용인시" : "L08",
  275. "성남시" : "L09",
  276. "고양시" : "L10",
  277. "시흥시" : "L11",
  278. "파주시" : "L12",
  279. "양주시" : "L13",
  280. "의정부시" : "L14",
  281. "김포시" : "L15",
  282. "의왕시" : "L16",
  283. "군포시" : "L17",
  284. "남양주시" : "L18",
  285. "수원시" : "L19",
  286. "광주시" : "L20",
  287. "구리시" : "L21",
  288. "하남시" : "L22",
  289. "부산시" : "L23",
  290. "양산시" : "L24",
  291. "창원시" : "L25",
  292. "김해시" : "L26",
  293. "화성시" : "L27",
  294. "거제시" : "L28",
  295. "대구시" : "L29",
  296. "대전시" : "L30",
  297. "광주광역시" : "L31",
  298. "포항시" : "L37"
  299. }
  300. // 메세지 정의
  301. let messageIndex = {
  302. "0" : "노드ID 미입력",
  303. "1" : "노드ID 중복",
  304. "2" : "노드ID 10자리수 초과",
  305. "3" : "교차로명 미입력",
  306. "4" : "위도 미입력",
  307. "5" : "형식에 맞지 않는 위도",
  308. "6" : "경도 미입력",
  309. "7" : "형식에 맞지 않는 경도",
  310. "8" : "지역/시(ADDR1) 미입력",
  311. "9" : "군/구(ADDR2) 미입력",
  312. "10" : "주소(ADDR3) 미입력",
  313. "11" : "연등지 항목 미입력",
  314. "12" : "NODE TYPE 미입력",
  315. "13" : "형식에 맞지 않는 NODE TYPE",
  316. "14" : "등록되어 있지 않은 지역"
  317. }
  318. // 전체 NODE 조회
  319. function allNodeId() {
  320. $.ajax({
  321. url: "/node/getAllNodeId.do",
  322. async: false,
  323. cache: false,
  324. type: "get",
  325. dataType: "json",
  326. success: function(result) {
  327. // var include = result.includes(value.toString());
  328. // checker.push(include);
  329. nodeIdData = result;
  330. }, error: function(error) {
  331. console.log(error)
  332. }
  333. })
  334. }
  335. // NodeVO 객체
  336. function NodeVO(nodeid, name, latitude, longitude, addr1, addr2,
  337. addr3, addnode, ipaddr, useyn, cvimyn, nodeyn, testyn,
  338. nodetype, regionId){
  339. this.nodeid = nodeid;
  340. this.name = name;
  341. this.latitude = latitude;
  342. this.longitude = longitude;
  343. this.addr1 = addr1;
  344. this.addr2 = addr2;
  345. this.addr3 = addr3;
  346. this.addnode = addnode;
  347. this.ipaddr = ipaddr;
  348. this.useyn = useyn;
  349. this.cvimyn = cvimyn;
  350. this.nodeyn = nodeyn;
  351. this.testyn = testyn;
  352. this.nodetype = nodetype;
  353. this.regionId = regionId;
  354. }
  355. // NULLCHECKER
  356. function nullChecker(item) {
  357. var result = (item == null || item == "" || item == undefined) ? true : false;
  358. return result;
  359. }
  360. function regionExcel() {
  361. $.when(regionArr(region)).done(
  362. readExcel()
  363. )
  364. }
  365. function readExcel() {
  366. addData = [];
  367. // 검사 여부 초기화
  368. validateYN = 0;
  369. // CSS 초기화
  370. $(".batch-popup")[0].style["grid-template-rows"] = "60px 30px 1fr"
  371. $(".inspection-option")[0].style["display"] = "none"
  372. $(window)[0].resizeTo(616, 380);
  373. // 엑셀 Parsing
  374. let input = event.target;
  375. let reader = new FileReader();
  376. reader.onload = function () {
  377. let data = reader.result;
  378. let workBook = XLSX.read(data, { type: 'binary' });
  379. workBook.SheetNames.forEach(function (sheetName) {
  380. let rows = XLSX.utils.sheet_to_json(workBook.Sheets[sheetName],
  381. {header:["NODE ID","교차로","위도","경도","지역(시)","군/구","주소","ADD NODE","IP 주소","사용 여부","CVIM TOPIC","NODE TOPIC","TEST TOPIC","NODE TYPE","권역 ID"]}
  382. );
  383. console.log(rows.length);
  384. rows.forEach((v, i) => {
  385. let nodeid = String(v['NODE ID'])
  386. let name = v['교차로']
  387. let latitude = String(v['위도'])
  388. let longitude = String(v['경도'])
  389. let addr1 = v['지역(시)']
  390. let addr2 = v['군/구']
  391. let addr3 = v['주소']
  392. let addnode = v['ADD NODE'] === '연등지' ? 'T' : 'F'
  393. let ipaddr = v['IP 주소']
  394. let useyn = v['사용 여부'] === '사용' ? 'Y' : 'N'
  395. let cvimyn = v['CVIM TOPIC'] === '사용' ? 'Y' : 'N'
  396. let nodeyn = String(v['NODE TOPIC'] === '사용' ? 'Y' : 'N')
  397. let testyn = v['TEST TOPIC'] === '사용' ? 'Y' : 'N'
  398. let nodetype = String(v['NODE TYPE'])
  399. let test = region[0].filter(value => value.region_name === v['권역 ID'])
  400. let regionId = test[0] ? test[0].region_id : null
  401. if(i > 1){
  402. addData.push(
  403. new NodeVO(
  404. nodeid,name,latitude,longitude,addr1,
  405. addr2,addr3,addnode,ipaddr,useyn
  406. ,cvimyn,nodeyn,testyn,nodetype,regionId
  407. )
  408. );
  409. }
  410. })
  411. })
  412. };
  413. reader.readAsBinaryString(input.files[0]);
  414. }
  415. function validateExcel(check){
  416. obj = null;
  417. delStr = ""
  418. result = [];
  419. resultD = [] // 중복 기반정보
  420. errList = [];
  421. message = "";
  422. $(".inspection-result")[0].innerText = "";
  423. addData.forEach((v, i) => {
  424. let cnt = 0;
  425. // 노드 조회(중복 검사)
  426. let checker = [];
  427. let validate = {
  428. nodeid : v.nodeid,
  429. name : v.name,
  430. err : new Array(15).fill(0)
  431. };
  432. checker.push(nodeIdData.includes(v['nodeid']));
  433. // allNodeId(v['nodeid'], checker)
  434. // NODE ID NULLCHECK
  435. if(nullChecker(v['nodeid'])){
  436. validate.err[0] = 1;
  437. cnt++;
  438. // alert("엑셀에 NODE ID가 입력되지 않은 항목이 존재합니다.");
  439. // return
  440. }
  441. // NODE ID 중복검사
  442. if(checker[0]){
  443. validate.err[1] = 1;
  444. cnt++;
  445. // alert("엑셀에 NODE ID가 중복된 항목이 존재합니다.")
  446. // return
  447. }
  448. if (check == 'upload2'){
  449. delStr += v['nodeid'] +','
  450. }
  451. // NODE ID 10자리 이하 숫자 검사
  452. if (!nodeIdPattern.test(v['nodeid'])) {
  453. validate.err[2] = 1;
  454. cnt++;
  455. // alert("엑셀에 NODE ID가 10자리 이상 입력된 항목이 존재합니다.");
  456. // return
  457. }
  458. // NAME NULLCHECK
  459. if (nullChecker(v['name'])) {
  460. validate.err[3] = 1;
  461. cnt++;
  462. // alert("엑셀에 교차로가 입력되지 않은 항목이 존재합니다.");
  463. // return
  464. }
  465. // 위도 NULLCHECK
  466. if (nullChecker(v['latitude'])) {
  467. validate.err[4] = 1;
  468. cnt++;
  469. // alert("엑셀에 위도가 입력되지 않은 항목이 존재합니다.");
  470. // return
  471. }
  472. // 위도 정규식 검사
  473. if (!latitudePattern.test(v['latitude'])) {
  474. validate.err[5] = 1;
  475. cnt++;
  476. // alert("엑셀에 위도 입력 시 소수점 포함 앞자리 0~2자리, 뒷자리 0~14자리로 입력해주세요.")
  477. // return
  478. }
  479. // 경도 NULLCHECK
  480. if (nullChecker(v['longitude'])) {
  481. validate.err[6] = 1;
  482. cnt++;
  483. // alert("엑셀에 경도가 입력되지 않은 항목이 존재합니다.");
  484. // return
  485. }
  486. // 경도 정규식 검사
  487. if (!longitudePattern.test(v['longitude'])) {
  488. validate.err[7] = 1;
  489. cnt++;
  490. // alert("엑셀에 경도 입력 시 소수점 포함 앞자리 0~3자리, 뒷자리 0~14자리로 입력해주세요.")
  491. // return
  492. }
  493. // 지역 NULLCHECK
  494. if (nullChecker(v['addr1'])) {
  495. validate.err[8] = 1;
  496. cnt++;
  497. // alert("엑셀에 지역(시)이 입력되지 않은 항목이 존재합니다.");
  498. // return
  499. }
  500. // 군/구 NULLCHECK
  501. if (nullChecker(v['addr2'])) {
  502. validate.err[9] = 1;
  503. cnt++;
  504. // alert("엑셀에 군(구)이 입력되지 않은 항목이 존재합니다.");
  505. // return
  506. }
  507. // 주소 NULLCHECK
  508. if (nullChecker(v['addr3'])) {
  509. validate.err[10] = 1;
  510. cnt++;
  511. // alert("엑셀에 주소가 입력되지 않은 항목이 존재합니다.");
  512. // return
  513. }
  514. // ADD NODE NULLCHECK
  515. if (nullChecker(v['addnode'])) {
  516. validate.err[11] = 1;
  517. cnt++;
  518. // alert("엑셀에 연등지 여부가 입력되지 않은 항목이 존재합니다.");
  519. // return
  520. }
  521. // NODE TYPE NULLCHECK
  522. if (nullChecker(v['nodetype'])) {
  523. validate.err[12] = 1;
  524. cnt++;
  525. // alert("엑셀에 NODE TYPE이 입력되지 않은 항목이 존재합니다.");
  526. // return
  527. }
  528. // NODE TYPE 정규식 검사
  529. if(!ntPattern.test(v['nodetype'])) {
  530. validate.err[13] = 1;
  531. cnt++;
  532. // alert("엑셀에 NODE TYPE 입력 시 형식에 맞게 입력하세요(숫자만 입력 가능)")
  533. // return
  534. }
  535. // 지역ID 검사
  536. if(v['regionId'] == null){
  537. validate.err[14] = 1;
  538. v['regionId'] = 'L01';
  539. // alert("입력하신 권역ID는 등록되지 않은 권역입니다. 기본 설정된 권역ID로 자동 입력됩니다.")
  540. // return
  541. }
  542. if(!cnt){
  543. result.push(v);
  544. } else {
  545. resultD.push(v);
  546. errList.push(validate);
  547. }
  548. })
  549. message = "※ 엑셀 데이터 검사 중 예외처리가 발생한 정보는 추가되지 않습니다.\n";
  550. errList.forEach((v1, i1) => {
  551. message += "\n노드ID : " + v1.nodeid + " / " + "교차로명 : " + v1.name
  552. + "\n\n<에러항목>\n";
  553. v1.err.forEach((v2, i2) => {
  554. if(v2){
  555. message += "○ " + messageIndex[i2] + "\n";
  556. }
  557. })
  558. })
  559. jQuery.ajaxSettings.traditional = true;
  560. $(".inspection-result")[0].innerText = message;
  561. validateYN = 1;
  562. }
  563. function addNode(arr1){
  564. let obj1 = JSON.stringify(arr1);
  565. if(arr1.length){
  566. $.ajax({
  567. url:"/node/addNode.do",
  568. async:false,
  569. type: "get",
  570. dataType: "json",
  571. data:{
  572. jsonData:obj1
  573. },
  574. success:function(result){
  575. if(result==0){
  576. alert("노드 등록이 정상적으로 처리되지 않았습니다.");
  577. return
  578. }
  579. alert("노드 정보 "+result+"건이 등록 되었습니다.");
  580. window.close();
  581. }, error: function(error) {
  582. console.log(error)
  583. }
  584. })
  585. } else {
  586. alert("등록할 정보가 존재하지 않습니다.")
  587. }
  588. }
  589. function updateNode(arr1){
  590. let obj1 = JSON.stringify(arr1);
  591. if(arr1.length){
  592. $.ajax({
  593. url:"/node/updateMultiNode.do",
  594. async:false,
  595. type: "get",
  596. dataType: "json",
  597. data:{
  598. jsonData:obj1
  599. },
  600. success:function(result){
  601. if(result==0){
  602. alert("노드 수정이 정상적으로 처리되지 않았습니다.");
  603. return
  604. }
  605. alert("노드 정보 "+result+"건이 수정 되었습니다.");
  606. window.close();
  607. }, error: function(error) {
  608. console.log(error)
  609. }
  610. })
  611. } else {
  612. alert("등록할 정보가 존재하지 않습니다.")
  613. }
  614. }
  615. function saveExcel(){
  616. if(addData.length){
  617. if(validateYN){
  618. // 중복 데이터 제외 후 추가
  619. if($("input[name='option']:checked").val() == 'upload1'){
  620. if(confirm(result.length + "건의 노드 정보를 추가 하시겠습니까?")){
  621. // 추가
  622. addNode(result)
  623. }
  624. } else {
  625. if(confirm(result.length + "건의 노드 정보 추가와 " + resultD.length + "건의 노드 정보 수정을 하시겠습니까?")){
  626. $.when(addNode(result)).done(
  627. updateNode(resultD)
  628. )
  629. }
  630. }
  631. } else {
  632. alert("엑셀 검사 후 등록이 가능합니다.")
  633. }
  634. } else if(!addData.length) {
  635. alert("엑셀이 등록되지 않았거나, 엑셀 데이터가 존재하지 않습니다.")
  636. }
  637. }
  638. </script>
  639. </body>
  640. </html>