From 16f48c10d09b328c8c25eb93932d4c19aca7ad86 Mon Sep 17 00:00:00 2001 From: yangsh Date: Tue, 30 Dec 2025 09:41:20 +0900 Subject: [PATCH] =?UTF-8?q?=EC=95=84=ED=8C=8C=ED=8A=B8=EB=8B=A8=EC=A7=80?= =?UTF-8?q?=20=EC=97=91=EC=85=80=EC=97=85=EB=A1=9C=EB=93=9C=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Config/Routes.php | 1 + app/Controllers/Article/Apt.php | 57 +++++++ app/Models/article/AptModel.php | 71 ++++++++ app/Views/pages/article/lists.php | 259 +++++++++++++++++++++++++++++- public/plugin/sample/sample.xlsx | Bin 0 -> 8632 bytes 5 files changed, 382 insertions(+), 6 deletions(-) create mode 100644 public/plugin/sample/sample.xlsx 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 0000000000000000000000000000000000000000..5152b00904b80e60bdcfdc83ff37519239274f06 GIT binary patch literal 8632 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 literal 0 HcmV?d00001