Files
confirms/app/Views/pages/results/m411/stats.php
2025-12-18 14:50:48 +09:00

447 lines
15 KiB
PHP

<?= $this->extend('layouts/main') ?>
<?= $this->section('content') ?>
<style>
th {
font-size: 11px;
}
#resultList tbody tr {
cursor: pointer;
}
.blockUI {
z-index: 1500 !important;
}
.swal2-cancel {
background-color: #ff0000 !important;
color: #fff !important;
}
</style>
<h1>확인매물 매체사 실적</h1>
<div class="row">
<div class="col-md-12 col-xl-12">
<div class="main-card mb-3 card">
<div class="card-body">
<form class="row align-items-end" id="frm_srch_info" onsubmit="return false;">
<!-- 1줄 -->
<div class="row mb-3">
<!-- 기준일자 -->
<div class="col-md-3">
<label class="form-label mb-1">기준일자</label>
<div class="row g-2">
<div class="col-5">
<input type="date" class="form-control" id="sdate" name="sdate">
</div>
<div class="col-2 d-flex align-items-center justify-content-center">~</div>
<div class="col-5">
<input type="date" class="form-control" id="edate" name="edate">
</div>
</div>
</div>
<!-- <div class="col-md-2">
<label class="form-label mb-1">팀별실적</label>
<select class="form-control" name="dept_sq">
<option value="">선택</option>
</select>
</div>
<div class="col-md-1">
<label class="form-label mb-1">검증방식</label>
<select class="form-control" name="vrfcreq_way">
<option value="">선택</option>
</select>
</div> -->
<div class="col-md-1 d-grid align-items-end">
<button type="button" class="btn btn-primary" id="btnSearch">검색</button>
</div>
</div>
</form>
</div>
</div>
</div>
<div class="col-md-12 col-xl-12">
<div class="main-card mb-3 card">
<div class="card-header d-flex align-items-center">
<h3 class="card-title mb-0">매체사 목록</h3>
<div class="ms-auto d-flex align-items-center gap-3">
<button class="mb-2 me-2 border-0 btn-transition btn btn-shadow btn-outline-success"
id="excel-download">엑셀다운로드</button>
</div>
</div>
<div class="card-body">
<table id="resultList" class="table table-hover table-striped table-bordered">
<thead>
<tr>
<th rowspan="2">매체사</th>
<th colspan="6" style="text-align: center;">접수</th>
<th colspan="3" style="text-align: center;">서류확인</th>
<th colspan="5" style="text-align: center;">전화확인</th>
<th colspan="5" style="text-align: center;">등기부확인</th>
<th colspan="2" style="text-align: center;">서비스</th>
</tr>
<tr>
<th>전화</th>
<th>확인서</th>
<th>확인서v2</th>
<th>모바일</th>
<th>모바일v2</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>
<tfoot class="table-light">
<tr class="fw-bold">
<td>합계</td>
<td class="sum-t_cnt"></td>
<td class="sum-hong1_cnt"></td>
<td class="sum-hong2_cnt"></td>
<td class="sum-m_cnt"></td>
<td class="sum-v2_cnt"></td>
<td class="sum-total"></td>
<td class="sum-d_o"></td>
<td class="sum-d_x"></td>
<td class="sum-d_tot"></td>
<td class="sum-t_o"></td>
<td class="sum-t_x"></td>
<td class="sum-t_n"></td>
<td class="sum-t_e"></td>
<td class="sum-t_tot"></td>
<td class="sum-r_o"></td>
<td class="sum-r_x"></td>
<td class="sum-r_e"></td>
<td class="sum-done"></td>
<td class="sum-r_tot"></td>
<td class="sum-s_1"></td>
<td class="sum-s_2"></td>
</tr>
</tfoot>
</table>
</div>
</div>
</div>
</div>
<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.13.6/css/jquery.dataTables.min.css" />
<script src="https://cdn.datatables.net/1.13.6/js/jquery.dataTables.min.js"></script>
<script defer src="/architectui/assets/js/datatable.kor.js"></script>
<script type="text/javascript">
// const tpl = document.querySelector('.my-loader-template');
let date = new Date();
$(function () {
let table = $('#resultList').DataTable({
language: lang_kor,
processing: true,
ajax: {
url: '/m411/m411a/getResultList',
type: 'GET',
beforeSend: function () {
blockUI.blockPage({
message: tpl
})
},
complete: function () {
blockUI.unblockPage()
},
data: function (d) {
d.sdate = $("#frm_srch_info [name=sdate]").val()
d.edate = $("#frm_srch_info [name=edate]").val()
d.start = d.start || 0
d.length = d.length || 10
}
},
"columnDefs": [
{ 'targets': '_all', "defaultContent": "" },
{ className: 'text-center', targets: '_all' },
],
columns: [
{ data: 'cpid', "width": "100px" },
{ data: 't_cnt' },
{ data: 'hong1_cnt' },
{ data: 'hong2_cnt' },
{ data: 'm_cnt' },
{ data: 'v2_cnt' },
{ data: 'total' },
{ data: 'd_o' },
{ data: 'd_x' },
{ data: 'd_tot' },
{ data: 't_o' },
{ data: 't_x' },
{ data: 't_n' },
{ data: 't_e' },
{ data: 't_tot' },
{ data: 'r_o' },
{ data: 'r_x' },
{ data: 'r_e' },
{ data: 'done' },
{ data: 'r_tot' },
{ data: 's_1' },
{ data: 's_2' },
],
// 옵션들 예시
paging: false,
searching: false,
ordering: false,
serverSide: true,
});
$('#resultList').on('xhr.dt', function (e, settings, json) {
const rows = json?.data || [];
const fields = [
't_cnt', 'hong1_cnt', 'hong2_cnt', 'm_cnt', 'v2_cnt', 'total'
, 'd_o', 'd_x', 'd_tot'
, 't_o', 't_x', 't_n', 't_e', 't_tot'
, 'r_o', 'r_x', 'r_e', 'done', 'r_tot'
, 's_1', 's_2'
];
const toNum = (v) => {
if (v == null || v === '') return 0;
return Number(String(v).replace(/,/g, '')) || 0; // "18" 같은 문자열 대응
};
// 합계 객체 생성
const sum = {};
fields.forEach(f => sum[f] = 0);
// rows 합산
rows.forEach(r => {
fields.forEach(f => sum[f] += toNum(r[f]));
});
// footer에 주입
fields.forEach(f => {
$('#resultList tfoot .sum-' + f).text(sum[f].toLocaleString());
});
});
$('#resultList tbody').on('click', 'tr', function () {
const row = table.row(this).data()
if (!row) return
});
// [검색] 버튼 눌렀을 때 다시 조회
$('#btnSearch').on('click', function () {
table.ajax.reload()
});
// 유저 등록 모달
$("#addUser").on("click", function () {
// $("#frm_user_info")[0].reset()
// $("#frm_user_info [name=usr_sq]").val("")
// $("#frm_user_info [name=type]").val("create")
// $("#frm_user_info [name=addUserId]").prop("readonly", false)
// const modalEl = document.getElementById('userModal');
// const myModal = new bootstrap.Modal(modalEl);
// myModal.show();
});
// 엑셀다운 click
$("#excel-download").on("click", function () {
$.ajax({
url: "/m411/m411a/excel",
method: "GET",
dataType: "json",
data: $("#frm_srch_info").serialize(),
beforeSend: function () {
blockUI.blockPage({
message: tpl
})
},
complete: function () {
blockUI.unblockPage()
},
success: function (result) {
// downloadExcel(result.data);
downloadExcelWithHeader(result.data);
}
});
});
});
// 엑셀 다운로드
function downloadExcelWithHeader(dataRows) {
/* =======================
* 1) 헤더 (총 22컬럼)
* ======================= */
const header1 = [
"매체사",
"접수", "", "", "", "", "",
"서류확인", "", "",
"전화확인", "", "", "", "",
"등기부확인", "", "", "", "",
"서비스", ""
];
const header2 = [
"",
"전화", "확인서", "확인서v2", "모바일", "모바일v2", "합계",
"일치", "불일치", "합계",
"일치", "불일치", "거부", "무응답외", "합계",
"일치", "불일치", "등기부없음", "검증완료", "합계",
"건수", "접수 대비"
];
// ✅ 컬럼 개수 고정(22)
const COLS = 22;
const fitCols = (arr) => {
const a = arr.slice(0, COLS);
while (a.length < COLS) a.push("");
return a;
};
// ✅ 숫자/빈값 안전 처리
const val = (v) => (v === undefined || v === null || v === "") ? 0 : v;
/* =======================
* 2) 바디 (DataTables columns 순서 그대로)
* ======================= */
const body = (dataRows || []).map(r => fitCols([
r.cpid ?? "", // 매체사
val(r.t_cnt),
val(r.hong1_cnt),
val(r.hong2_cnt),
val(r.m_cnt),
val(r.v2_cnt),
val(r.total),
val(r.d_o),
val(r.d_x),
val(r.d_tot),
val(r.t_o),
val(r.t_x),
val(r.t_n),
val(r.t_e),
val(r.t_tot),
val(r.r_o),
val(r.r_x),
val(r.r_e),
val(r.done),
val(r.r_tot),
val(r.s_1),
val(r.s_2),
]));
const aoa = [fitCols(header1), fitCols(header2), ...body];
const ws = XLSX.utils.aoa_to_sheet(aoa);
/* =======================
* 3) 병합(merge) 설정 (thead와 동일)
* ======================= */
ws["!merges"] = [
// 매체사 rowspan 2
{ s: { r: 0, c: 0 }, e: { r: 1, c: 0 } },
// 접수 colspan 6 (1~6)
{ s: { r: 0, c: 1 }, e: { r: 0, c: 6 } },
// 서류확인 colspan 3 (7~9)
{ s: { r: 0, c: 7 }, e: { r: 0, c: 9 } },
// 전화확인 colspan 5 (10~14)
{ s: { r: 0, c: 10 }, e: { r: 0, c: 14 } },
// 등기부확인 colspan 5 (15~19)
{ s: { r: 0, c: 15 }, e: { r: 0, c: 19 } },
// 서비스 colspan 2 (20~21)
{ s: { r: 0, c: 20 }, e: { r: 0, c: 21 } },
];
/* =======================
* 4) 컬럼 너비(가독성)
* ======================= */
ws["!cols"] = [
{ wpx: 110 }, // 매체사
{ wpx: 60 }, { wpx: 70 }, { wpx: 80 }, { wpx: 70 }, { wpx: 80 }, { wpx: 60 }, // 접수 6
{ wpx: 60 }, { wpx: 60 }, { wpx: 60 }, // 서류 3
{ wpx: 60 }, { wpx: 60 }, { wpx: 60 }, { wpx: 70 }, { wpx: 60 }, // 전화 5
{ wpx: 60 }, { wpx: 60 }, { wpx: 80 }, { wpx: 80 }, { wpx: 60 }, // 등기부 5
{ wpx: 70 }, { wpx: 80 }, // 서비스 2
];
/* =======================
* 5) 저장
* ======================= */
const wb = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, ws, "sheet1");
XLSX.writeFile(wb, "확인매물_매체사_실적_" + getDateTimeString() + ".xlsx");
}
function getDateTimeString() {
const d = new Date();
const yyyy = d.getFullYear();
const mm = String(d.getMonth() + 1).padStart(2, '0');
const dd = String(d.getDate()).padStart(2, '0');
const hh = String(d.getHours()).padStart(2, '0');
const mi = String(d.getMinutes()).padStart(2, '0');
const ss = String(d.getSeconds()).padStart(2, '0');
return `${yyyy}${mm}${dd}${hh}${mi}${ss}`;
}
document.addEventListener("DOMContentLoaded", function () {
const today = new Date().toISOString().slice(0, 10);
document.getElementById("sdate").value = today;
document.getElementById("edate").value = today;
});
</script>
<?= $this->endSection() ?>