From 48f4c0e15897cb1603a3b3cf40347989b0c2a143 Mon Sep 17 00:00:00 2001 From: yangsh Date: Tue, 30 Dec 2025 11:06:23 +0900 Subject: [PATCH] =?UTF-8?q?=EC=95=84=ED=8C=8C=ED=8A=B8=ED=8F=89=EB=A9=B4?= =?UTF-8?q?=EB=8F=84=20=EC=97=91=EC=85=80=EC=97=85=EB=A1=9C=EB=93=9C=20?= =?UTF-8?q?=EC=B6=94=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/Ground.php | 51 ++++ app/Models/article/GroundModel.php | 27 +++ app/Views/pages/article/lists2.php | 247 ++++++++++++++++++++ public/plugin/sample/sample_apt_ground.xlsx | Bin 0 -> 10341 bytes 5 files changed, 326 insertions(+) create mode 100644 public/plugin/sample/sample_apt_ground.xlsx diff --git a/app/Config/Routes.php b/app/Config/Routes.php index bc16274..5686df9 100644 --- a/app/Config/Routes.php +++ b/app/Config/Routes.php @@ -103,6 +103,7 @@ $routes->group('article', ['namespace' => 'App\Controllers\Article'], function ( */ $routes->get('apt/ground/getAptLists', 'Ground::getAptLists'); $routes->get('apt/ground/excel', 'Ground::excel'); + $routes->post('apt/ground/uploadExcel', 'Ground::uploadExcel'); $routes->post('apt/ground/chgAptDamdang', 'Ground::chgAptDamdang'); $routes->post('apt/ground/uploadFile', 'Ground::uploadFile'); $routes->get('apt/ground/print', 'Ground::print'); diff --git a/app/Controllers/Article/Ground.php b/app/Controllers/Article/Ground.php index beafaae..f374494 100644 --- a/app/Controllers/Article/Ground.php +++ b/app/Controllers/Article/Ground.php @@ -84,6 +84,57 @@ class Ground 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 = [ + 'hscp_no' => $data[1], + 'region_cd' => $data[2], + 'part_no' => $data[0], + 'apt_step' => 'S01', + 'addr' => $data[3] . ' ' . $data[4] . ' ' . $data[5], + 'addr2' => $data[6], + 'rcpt_hscp_nm' => $data[7], + 'apt_cate_nm' => $data[8], + 'pyeong_cnt' => $data[9], + 'rcpt_x' => $data[10], + 'rcpt_y' => $data[11], + 'ginsert_tm' => $rdate, + ]; + + // INSERT apt_ground + $this->model->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/GroundModel.php b/app/Models/article/GroundModel.php index cd57b5a..790533f 100644 --- a/app/Models/article/GroundModel.php +++ b/app/Models/article/GroundModel.php @@ -468,6 +468,33 @@ class GroundModel extends Model return $query->getResultArray(); } + // 엑셀 업로드 저장 + public function saveExcelUploadData($params) + { + $this->db->transStart(); + + $builder = $this->db->table('apt_ground'); + $res = $builder->insert($params); + + if ($res === false) { + return [ + 'success' => false, + 'msg' => "구분코드 : {$params['part_no']} 저장실패", + ]; + } + + $rcpt_no = $this->db->insertID(); + + $this->saveHistory($rcpt_no, $params['apt_step'], 'U', 'U1', session('usr_id')); + + $this->db->transComplete(); + + // 성공 + return [ + 'success' => true, + ]; + } + // 엑셀 다운로드 public function getExcelList($data) { diff --git a/app/Views/pages/article/lists2.php b/app/Views/pages/article/lists2.php index f2de5df..3b6655b 100644 --- a/app/Views/pages/article/lists2.php +++ b/app/Views/pages/article/lists2.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; + }

아파트 평면도 내역

@@ -279,6 +288,10 @@
+ + @@ -386,6 +399,69 @@
+ + endSection() ?> @@ -685,6 +761,137 @@ 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); + + }); + + + // 엑셀 업로드 저장 + $("#btnExcelUpload").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/ground/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 () { @@ -984,6 +1191,46 @@ }); + 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() diff --git a/public/plugin/sample/sample_apt_ground.xlsx b/public/plugin/sample/sample_apt_ground.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..e44fed74103a607b6ceedd85c4853fa0aa9a6b81 GIT binary patch literal 10341 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 literal 0 HcmV?d00001