diff --git a/app/Config/Routes.php b/app/Config/Routes.php index 2c672c8..bc16274 100644 --- a/app/Config/Routes.php +++ b/app/Config/Routes.php @@ -61,6 +61,7 @@ $routes->group('article', ['namespace' => 'App\Controllers\Article'], function ( $routes->post('apt/chgAptVideoTarget', 'Apt::chgAptVideoTarget'); $routes->post('apt/chkTakeAptPhotoCnt', 'Apt::chkTakeAptPhotoCnt'); $routes->get('apt/excel', 'Apt::excel'); + $routes->post('apt/uploadExcel', 'Apt::uploadExcel'); /** API - 아파트단지 상세 */ $routes->post('apt/saveKeeper', 'Apt::saveKeeper'); diff --git a/app/Controllers/Article/Apt.php b/app/Controllers/Article/Apt.php index 1d6b958..311a6d9 100644 --- a/app/Controllers/Article/Apt.php +++ b/app/Controllers/Article/Apt.php @@ -267,6 +267,63 @@ class Apt extends BaseController } } + // 엑셀 업로드 + public function uploadExcel() + { + try { + + $payload = $this->request->getJSON(true); + $datas = $payload['datas'] ?? null; + + if (count($datas) === 0) { + return $this->response->setJSON([ + 'code' => '9', + 'msg' => "데이터 없음", + ]); + } + + + foreach ($datas as $data) { + $rdate = date("Y-m-d H:i:s"); + + $params = [ + 'row_no' => $data[0], + 'hscp_no' => $data[1], + 'uni_hscp_no' => $data[2], + 'region_cd' => $data[3], + 'addr' => $data[4], + 'addr2' => $data[5], + 'apt_cate_nm' => $data[6], + 'rcpt_hscp_nm' => $data[7], + 'move_ym' => $data[8], + 'households_cnt' => $data[9], + 'dong_cnt' => $data[10], + 'pyeong_cnt' => $data[11], + 'dongho' => $data[12], + 'use_yn' => $data[13], + 'rcpt_x' => $data[14], + 'rcpt_y' => $data[15], + 'pho_exept_yn' => $data[16], + 'rdate' => $rdate, + ]; + + // INSERT apt_receipt, apt_result + $this->aptModel->saveExcelUploadData($params); + + } + + return $this->response->setJSON([ + 'code' => '0', + 'msg' => 'success' + ]); + + } catch (\Exception $e) { + return $this->response->setJSON([ + 'code' => '9', + 'msg' => $e->getMessage(), + ]); + } + } // 엑셀 다운로드 public function excel() diff --git a/app/Models/article/AptModel.php b/app/Models/article/AptModel.php index 4704df6..071e356 100644 --- a/app/Models/article/AptModel.php +++ b/app/Models/article/AptModel.php @@ -683,6 +683,77 @@ class AptModel extends Model ]; } + // 엑셀업로드 저장 + public function saveExcelUploadData($params) + { + + $this->db->transStart(); + + $builder = $this->db->table('apt_receipt'); + $res = $builder->insert($params); + + if ($res === false) { + return [ + 'success' => false, + 'msg' => "단지코드 : {$params['hscp_no']} 저장실패", + ]; + } + + $rcpt_no = $this->db->insertID(); + + $params2 = [ + 'rcpt_no' => $rcpt_no, + 'hscp_no' => $params['hscp_no'], + 'region_cd' => $params['region_cd'], + 'charger' => '', + 'dept_sq' => '', + 'addr' => $params['addr'], + 'addr2' => $params['addr2'], + 'rcpt_hscp_nm' => $params['rcpt_hscp_nm'], + 'rcpt_x' => $params['rcpt_x'], + 'rcpt_y' => $params['rcpt_y'], + 'move_ym' => $params['move_ym'], + 'households_cnt' => $params['households_cnt'], + 'dong_cnt' => $params['dong_cnt'], + 'apt_cate_nm' => $params['apt_cate_nm'], + 'apt_step' => 'S01', + 'check_yn' => 'N', + 'resend_yn' => 'N', + 'memo' => '', + 'pho_up_yn' => 'N', + 'vdo_up_ynx' => 'N', + 'vdo_up_tm' => NULL, + 'video_target' => 'N', + 'not_vdo_reson' => '', + 'note' => '', + 'not_vdo_tm' => NULL, + 'check_tm' => NULL, + 'write_complete_yn' => 'N', + 'write_complete_tm' => NULL, + 'all_no_pho' => NULL, + 'syncid' => '', + 'sync_comp' => NULL, + 'sync_wait_cnt' => '0' + ]; + + $builder = $this->db->table('apt_result'); + $res = $builder->insert($params2); + + if ($res === false) { + return [ + 'success' => false, + 'msg' => "단지코드 : {$params['hscp_no']} 저장실패", + ]; + } + + $this->db->transComplete(); + + // 성공 + return [ + 'success' => true, + ]; + } + // 엑셀 다운로드 public function getExcelList($data) { diff --git a/app/Views/pages/article/lists.php b/app/Views/pages/article/lists.php index 7dee47d..6cacb11 100644 --- a/app/Views/pages/article/lists.php +++ b/app/Views/pages/article/lists.php @@ -44,6 +44,15 @@ background-color: #ff0000 !important; color: #fff !important; } + + #excelList.dataTable { + width: max-content !important; + } + + table.dataTable td, + table.dataTable th { + white-space: nowrap; + }

아파트단지 DB구축 현황

@@ -325,6 +334,10 @@
+ + @@ -375,6 +388,72 @@
+ + endSection() ?> @@ -389,7 +468,7 @@ const teamArr = ; const userArr = ; - var table; + var table, excelTbl; $(function () { @@ -677,6 +756,136 @@ table.ajax.reload() }); + // 엑셀업로드 click + $("#excel-upload").on("click", function () { + $("#excel").val(""); + $("#excelList").DataTable().clear().destroy(); + + $("#excelModal").modal("show"); + + }); + + // 엑셀 업로드 + $("#excel").on("change", async function () { + const file = this.files[0]; + if (!file) return; + + const bodyRows = await readExcelBodyOnly(file); + renderExcelDataTable(bodyRows); + + }); + + // 엑셀 업로드 저장 + $("#btnUpload").on("click", function () { + + const dt = $("#excelList").DataTable(); + + // 전체 row 데이터 (모든 페이지) + const excelData = dt.rows().data().toArray(); + + if (excelData.length === 0) { + Swal.fire({ + title: "업로드할 데이터가 없습니다.", + icon: "warning" + }); + return; + } + + const headers = [ + "줄번호", "단지코드", "통합단지코드", "법정동코드", "주소" + , "상세주소", "단지유형", "단지명" + , "입주년월", "총세대수", "총동수", "평형수", "동호수" + , "사용여부", "경도", "위도", "사진촬영제외" + ]; + + const errors = []; + let hasError = false; + for (let rowIdx = 0; rowIdx < excelData.length; rowIdx++) { + const row = excelData[rowIdx]; + + for (let colIdx = 0; colIdx < headers.length; colIdx++) { + const colName = headers[colIdx]; + const val = row[colIdx]; + + + if (val === undefined || val === null || String(val).trim() === "") { + Swal.fire({ + title: `${rowIdx + 1}행 ${colName} 데이터 누락`, + icon: "warning" + }); + + hasError = true; + break; + } + } + + if (hasError) break; + } + + if (hasError) return; + + swal.fire({ + text: "저장 하시겠습니까?", + type: "warning", + showCancelButton: true, + confirmButtonText: "예", + cancelButtonText: "아니오", + closeOnConfirm: false, + closeOnCancel: true, + confirmButtonColor: "#3085d6", + cancelButtonColor: "#d33", + }).then((result) => { + if (result.isConfirmed) { + $.ajax({ + url: '/article/apt/uploadExcel', + contentType: 'application/json;charset=UTF-8', + method: 'POST', + data: JSON.stringify({ datas: excelData }), + beforeSend: function () { + blockUI.blockPage({ + message: tpl + }) + }, + complete: function () { + blockUI.unblockPage() + }, + error: function (xhr, error, thrown) { + blockUI.unblockPage() + var msg = ""; + if (xhr.responseText != null) { + msg = xhr.responseText + } else { + msg = "잠시후 다시 시도해 주세요." + } + + Swal.fire({ + title: msg, + icon: "error" + }) + }, + success: function (result) { + + if (result.code == '0') { + $("#btnSearch").trigger('click') + $("#excelModal").modal("hide") + Swal.fire({ + title: '정상 처리되었습니다.', + icon: "success" + + }) + } else { + Swal.fire({ + title: result.msg, + icon: "error" + }) + } + } + }); + } + }); + + + }); // 엑셀 다운로드 click $("#excel-download").on("click", function () { @@ -817,7 +1026,7 @@ Swal.fire({ title: msg, icon: "error" - }) + }); }, success: function (result) { @@ -940,6 +1149,48 @@ }); + async function readExcelBodyOnly(file) { + const data = await file.arrayBuffer(); + const wb = XLSX.read(data, { type: "array" }); + const ws = wb.Sheets[wb.SheetNames[0]]; + + const rows2d = XLSX.utils.sheet_to_json(ws, { + header: 1, + defval: "", + raw: false + }); + + // 첫 줄(엑셀 헤더) 제거 + 빈 행 제거 + return rows2d + .slice(1) + .filter(r => r.some(v => String(v).trim() !== "")); + } + + function renderExcelDataTable(data2d) { + // 이미 DataTable이 있으면 파괴 후 재생성(컬럼 구조가 바뀔 수 있으니) + if ($.fn.DataTable.isDataTable("#excelList")) { + $("#excelList").DataTable().clear().destroy(); + } + + excelTbl = $("#excelList").DataTable({ + language: lang_kor, + columnDefs: [ + { className: 'text-center dt-nowrap', targets: '_all' }, + ], + data: data2d, + searching: false, + ordering: false, + destroy: true, + deferRender: true, + pageLength: 10, + lengthMenu: [10, 25, 50, 100], + scrollX: true, + autoWidth: true, + }); + } + + + function hscp_no_enter(event) { if (event.keyCode == 13) { table.ajax.reload() @@ -1055,10 +1306,6 @@ // 단지정보저장 function fn_save_info(row, idx) { - console.log('+++') - console.log(row) - console.log('idx ?? ' + idx) - console.log('+++') const target = $("#video_target_" + idx).val(); console.log(target) diff --git a/public/plugin/sample/sample.xlsx b/public/plugin/sample/sample.xlsx new file mode 100644 index 0000000..5152b00 Binary files /dev/null and b/public/plugin/sample/sample.xlsx differ