아파트평면도 엑셀업로드 추가
Some checks failed
Close Pull Request / main (pull_request_target) Has been cancelled

This commit is contained in:
yangsh
2025-12-30 11:06:23 +09:00
parent 0f5d52271f
commit 48f4c0e158
5 changed files with 326 additions and 0 deletions

View File

@@ -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');

View File

@@ -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()
{

View File

@@ -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)
{

View File

@@ -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;
}
</style>
<h1>아파트 평면도 내역</h1>
@@ -279,6 +288,10 @@
</div>
<div class="ml-auto">
<button class="btn btn-sm btn-outline-secondary" id="excel-upload">
<i class="fa fa-fw" aria-hidden="true" title="file-excel-o"></i> 엑셀업로드
</button>
<button class="btn btn-sm btn-outline-success" id="excel-download">
<i class="fa fa-fw" aria-hidden="true" title="file-excel-o"></i> 엑셀다운로드
</button>
@@ -386,6 +399,69 @@
</div>
</div>
</div>
<div class="modal fade" id="excelModal" tabindex="-1">
<div class="modal-dialog modal-xl">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">엑셀 업로드</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<!-- 업로드 영역 -->
<div class="d-flex align-items-center gap-3 mb-3">
<input type="file" id="excel" class="form-control" accept=".xlsx,.xls,.csv"
style="max-width: 320px;">
<a href="/plugin/sample/sample_apt.xlsx" download="아파트단지_샘플양식"
class="btn btn-outline-secondary btn-sm">
샘플양식 다운로드
</a>
</div>
<!-- 안내 문구 -->
<div class="alert alert-light py-2 mb-3">
<ul class="mb-0 small">
<li>엑셀 첫 행은 헤더로 인식됩니다.</li>
<li>컬럼 순서 및 개수는 샘플양식과 동일해야 합니다.</li>
</ul>
</div>
<!-- 테이블 영역 -->
<div class="table-responsive">
<table id="excelList"
class="table table-sm table-hover table-striped table-bordered align-middle text-center mb-0">
<thead class="table-light">
<tr>
<th>구분코드</th>
<th>단지코드</th>
<th>법정동코드</th>
<th>시/도</th>
<th>시/군/구</th>
<th>읍/면/동</th>
<th>상세주소</th>
<th>단지명</th>
<th>구분명</th>
<th>평수</th>
<th>총동수</th>
<th>경도</th>
<th>위도</th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">닫기</button>
<button type="button" class="btn btn-primary" id="btnExcelUpload">저장</button>
</div>
</div>
</div>
</div>
<?= $this->endSection() ?>
<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.13.6/css/jquery.dataTables.min.css" />
@@ -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()

Binary file not shown.