아파트단지 엑셀업로드추가

This commit is contained in:
yangsh
2025-12-30 09:41:20 +09:00
parent cd1e4df4eb
commit 16f48c10d0
5 changed files with 382 additions and 6 deletions

View File

@@ -61,6 +61,7 @@ $routes->group('article', ['namespace' => 'App\Controllers\Article'], function (
$routes->post('apt/chgAptVideoTarget', 'Apt::chgAptVideoTarget'); $routes->post('apt/chgAptVideoTarget', 'Apt::chgAptVideoTarget');
$routes->post('apt/chkTakeAptPhotoCnt', 'Apt::chkTakeAptPhotoCnt'); $routes->post('apt/chkTakeAptPhotoCnt', 'Apt::chkTakeAptPhotoCnt');
$routes->get('apt/excel', 'Apt::excel'); $routes->get('apt/excel', 'Apt::excel');
$routes->post('apt/uploadExcel', 'Apt::uploadExcel');
/** API - 아파트단지 상세 */ /** API - 아파트단지 상세 */
$routes->post('apt/saveKeeper', 'Apt::saveKeeper'); $routes->post('apt/saveKeeper', 'Apt::saveKeeper');

View File

@@ -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() public function excel()

View File

@@ -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) public function getExcelList($data)
{ {

View File

@@ -44,6 +44,15 @@
background-color: #ff0000 !important; background-color: #ff0000 !important;
color: #fff !important; color: #fff !important;
} }
#excelList.dataTable {
width: max-content !important;
}
table.dataTable td,
table.dataTable th {
white-space: nowrap;
}
</style> </style>
<h1>아파트단지 DB구축 현황</h1> <h1>아파트단지 DB구축 현황</h1>
@@ -325,6 +334,10 @@
</div> </div>
<div class="ml-auto"> <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"> <button class="btn btn-sm btn-outline-success" id="excel-download">
<i class="fa fa-fw" aria-hidden="true" title="file-excel-o"></i> 엑셀다운로드 <i class="fa fa-fw" aria-hidden="true" title="file-excel-o"></i> 엑셀다운로드
</button> </button>
@@ -375,6 +388,72 @@
</div> </div>
</div> </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.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>
<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="btnUpload">저장</button>
</div>
</div>
</div>
</div>
<?= $this->endSection() ?> <?= $this->endSection() ?>
<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.13.6/css/jquery.dataTables.min.css" /> <link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.13.6/css/jquery.dataTables.min.css" />
@@ -389,7 +468,7 @@
const teamArr = <?= json_encode($team, JSON_UNESCAPED_UNICODE); ?>; const teamArr = <?= json_encode($team, JSON_UNESCAPED_UNICODE); ?>;
const userArr = <?= json_encode($user, JSON_UNESCAPED_UNICODE); ?>; const userArr = <?= json_encode($user, JSON_UNESCAPED_UNICODE); ?>;
var table; var table, excelTbl;
$(function () { $(function () {
@@ -677,6 +756,136 @@
table.ajax.reload() 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 // 엑셀 다운로드 click
$("#excel-download").on("click", function () { $("#excel-download").on("click", function () {
@@ -817,7 +1026,7 @@
Swal.fire({ Swal.fire({
title: msg, title: msg,
icon: "error" icon: "error"
}) });
}, },
success: function (result) { 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) { function hscp_no_enter(event) {
if (event.keyCode == 13) { if (event.keyCode == 13) {
table.ajax.reload() table.ajax.reload()
@@ -1055,10 +1306,6 @@
// 단지정보저장 // 단지정보저장
function fn_save_info(row, idx) { function fn_save_info(row, idx) {
console.log('+++')
console.log(row)
console.log('idx ?? ' + idx)
console.log('+++')
const target = $("#video_target_" + idx).val(); const target = $("#video_target_" + idx).val();
console.log(target) console.log(target)

Binary file not shown.