479 lines
18 KiB
PHP
479 lines
18 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>
|
|
<?php foreach ($department as $key => $d): ?>
|
|
<option value="<?= $d['dept_sq'] ?>"><?= $d['dept_nm'] ?></option>
|
|
<?php endforeach; ?>
|
|
</select>
|
|
</div>
|
|
|
|
<div class="col-md-1">
|
|
<label class="form-label mb-1">검증방식</label>
|
|
<select class="form-control" name="vrfcreq_way">
|
|
<option value="">선택</option>
|
|
<?php foreach ($code_vrfcreq_way as $cd => $cd_nm): ?>
|
|
<option value="<?= esc($cd) ?>"><?= esc($cd_nm) ?></option>
|
|
<?php endforeach; ?>
|
|
</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="2" style="text-align: center;">서류확인</th>
|
|
<th colspan="3" style="text-align: center;">전화확인</th>
|
|
<th colspan="5" style="text-align: center;">등기부등본</th>
|
|
<th colspan="2" style="text-align: center;">리얼탑 열람</th>
|
|
<th colspan="2" style="text-align: center;">리얼탑 기열람</th>
|
|
<th rowspan="2">모바일 확인</th>
|
|
<th rowspan="2">등기부등본<br />없음</th>
|
|
<th colspan="2" style="text-align: center;">검증</th>
|
|
</tr>
|
|
<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>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
</tbody>
|
|
|
|
<tfoot class="table-light">
|
|
<tr class="fw-bold">
|
|
<td>합계</td>
|
|
<td class="sum-paper_complete_cnt"></td>
|
|
<td class="sum-paper_fail_cnt"></td>
|
|
<td class="sum-phone_all_cnt"></td>
|
|
<td class="sum-phone_complete_cnt"></td>
|
|
<td class="sum-phone_fail_cnt"></td>
|
|
<td class="sum-reg_complete_cnt"></td>
|
|
<td class="sum-same_reg_open"></td>
|
|
<td class="sum-reg_fail_Open"></td>
|
|
<td class="sum-same_reg_tempOpen"></td>
|
|
<td class="sum-reg_fail_tempOpen"></td>
|
|
<td class="sum-real_top_RS"></td>
|
|
<td class="sum-real_top_RF"></td>
|
|
<td class="sum-real_top_GS"></td>
|
|
<td class="sum-real_top_GF"></td>
|
|
<td class="sum-mobile_upload"></td>
|
|
<td class="sum-reg_none"></td>
|
|
<td class="sum-complete_cnt"></td>
|
|
<td class="sum-fail_cnt"></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: '/m410/m410a/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.vrfcreq_way = $("#frm_srch_info [name=vrfcreq_way]").val()
|
|
d.dept_sq = $("#frm_srch_info [name=dept_sq]").val()
|
|
|
|
d.start = d.start || 0
|
|
d.length = d.length || 10
|
|
}
|
|
},
|
|
"columnDefs": [
|
|
{ 'targets': '_all', "defaultContent": "" },
|
|
{ className: 'text-center', targets: '_all' },
|
|
],
|
|
columns: [
|
|
{ data: 'usr_nm', "width": "100px" },
|
|
{ data: 'paper_complete_cnt' },
|
|
{ data: 'paper_fail_cnt' },
|
|
{ data: 'phone_all_cnt' },
|
|
{ data: 'phone_complete_cnt' },
|
|
{ data: 'phone_fail_cnt' },
|
|
{ data: 'reg_complete_cnt' },
|
|
{ data: 'same_reg_open' },
|
|
{ data: 'reg_fail_Open' },
|
|
{ data: 'same_reg_tempOpen' },
|
|
{ data: 'reg_fail_tempOpen' },
|
|
{ data: 'real_top_RS' },
|
|
{ data: 'real_top_RF' },
|
|
{ data: 'real_top_GS' },
|
|
{ data: 'real_top_GF' },
|
|
{ data: 'mobile_upload' },
|
|
{ data: 'reg_none' },
|
|
{ data: 'complete_cnt' },
|
|
{ data: 'fail_cnt' },
|
|
],
|
|
// 옵션들 예시
|
|
paging: false,
|
|
searching: false,
|
|
ordering: false,
|
|
serverSide: true,
|
|
});
|
|
|
|
|
|
$('#resultList').on('xhr.dt', function (e, settings, json) {
|
|
const rows = json?.data || [];
|
|
|
|
|
|
const fields = [
|
|
'paper_complete_cnt', 'paper_fail_cnt', 'phone_all_cnt', 'phone_complete_cnt', 'phone_fail_cnt'
|
|
, 'reg_complete_cnt', 'same_reg_open', 'reg_fail_Open', 'same_reg_tempOpen', 'reg_fail_tempOpen'
|
|
, 'real_top_RS', 'real_top_RF', 'real_top_GS', 'real_top_GF'
|
|
, 'mobile_upload', 'reg_none', 'complete_cnt', 'fail_cnt'
|
|
];
|
|
|
|
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
|
|
|
|
// const modalEl = document.getElementById('userModal');
|
|
// const myModal = new bootstrap.Modal(modalEl);
|
|
|
|
// $("#frm_user_info")[0].reset()
|
|
|
|
// $("#frm_user_info [name=usr_sq]").val(row.usr_sq)
|
|
// $("#frm_user_info [name=type]").val("update")
|
|
|
|
// $("#frm_user_info [name=addUserNm]").val(row.usr_nm)
|
|
// $("#frm_user_info [name=addUserDept]").val(row.dept_sq)
|
|
// $("#frm_user_info [name=addUserId]").val(row.usr_id)
|
|
// $("#frm_user_info [name=addUserId]").prop("readonly", true)
|
|
|
|
// $("#frm_user_info [name=addUserLevel]").val(row.usr_level)
|
|
// $("#frm_user_info [name=addUserPosition]").val(row.usr_position)
|
|
// $("#frm_user_info [name=addUserTel1]").val(row.usr_tel1)
|
|
// $("#frm_user_info [name=addUserTel2]").val(row.usr_tel2)
|
|
// $("#frm_user_info [name=addSmsYn]").val(row.sms_auth_yn)
|
|
// $("#frm_user_info [name=addUserAddr1]").val(row.usr_addr1)
|
|
// $("#frm_user_info [name=addUserAddr2]").val(row.usr_addr2)
|
|
// $("#frm_user_info [name=addUseYn]").val(row.use_yn)
|
|
|
|
// myModal.show();
|
|
|
|
});
|
|
|
|
// [검색] 버튼 눌렀을 때 다시 조회
|
|
$('#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: "/m410/m410a/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) {
|
|
|
|
const header1 = [
|
|
"상담원",
|
|
"서류확인", "",
|
|
"전화확인", "", "",
|
|
"등기부등본", "", "", "", "",
|
|
"리얼탑 열람", "",
|
|
"리얼탑 기열람", "",
|
|
"모바일 확인",
|
|
"등기부등본 없음",
|
|
"검증", ""
|
|
];
|
|
|
|
const header2 = [
|
|
"",
|
|
"완료", "실패",
|
|
"건수", "완료", "실패",
|
|
"확인완료", "일치열람", "열람 불일치", "일치 가열람", "가열람 불일치",
|
|
"일치", "불일치",
|
|
"일치", "불일치",
|
|
"",
|
|
"",
|
|
"완료", "실패"
|
|
];
|
|
|
|
// ✅ 컬럼 개수 고정 (19)
|
|
const COLS = 19;
|
|
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;
|
|
|
|
const body = (dataRows || []).map(r => fitCols([
|
|
r.usr_nm ?? r.usr_id ?? "",
|
|
val(r.paper_complete_cnt),
|
|
val(r.paper_fail_cnt),
|
|
val(r.phone_all_cnt),
|
|
val(r.phone_complete_cnt),
|
|
val(r.phone_fail_cnt),
|
|
val(r.reg_complete_cnt),
|
|
val(r.same_reg_open),
|
|
val(r.reg_fail_Open),
|
|
val(r.same_reg_tempOpen),
|
|
val(r.reg_fail_tempOpen),
|
|
val(r.real_top_RS),
|
|
val(r.real_top_RF),
|
|
val(r.real_top_GS),
|
|
val(r.real_top_GF),
|
|
val(r.mobile_upload),
|
|
val(r.reg_none),
|
|
val(r.complete_cnt), // ✅ 검증 완료
|
|
val(r.fail_cnt), // ✅ 검증 실패
|
|
]));
|
|
|
|
const aoa = [fitCols(header1), fitCols(header2), ...body];
|
|
const ws = XLSX.utils.aoa_to_sheet(aoa);
|
|
|
|
ws["!merges"] = [
|
|
{ s: { r: 0, c: 0 }, e: { r: 1, c: 0 } }, // 상담원 rowspan 2
|
|
{ s: { r: 0, c: 1 }, e: { r: 0, c: 2 } }, // 서류확인 colspan 2
|
|
{ s: { r: 0, c: 3 }, e: { r: 0, c: 5 } }, // 전화확인 colspan 3
|
|
{ s: { r: 0, c: 6 }, e: { r: 0, c: 10 } }, // 등기부등본 colspan 5
|
|
{ s: { r: 0, c: 11 }, e: { r: 0, c: 12 } }, // 리얼탑 열람 colspan 2
|
|
{ s: { r: 0, c: 13 }, e: { r: 0, c: 14 } }, // 리얼탑 기열람 colspan 2
|
|
{ s: { r: 0, c: 15 }, e: { r: 1, c: 15 } }, // 모바일 rowspan 2
|
|
{ s: { r: 0, c: 16 }, e: { r: 1, c: 16 } }, // 등기부없음 rowspan 2
|
|
{ s: { r: 0, c: 17 }, e: { r: 0, c: 18 } }, // ✅ 검증 colspan 2
|
|
];
|
|
|
|
ws["!cols"] = [
|
|
{ wpx: 90 },
|
|
{ wpx: 60 }, { wpx: 60 },
|
|
{ wpx: 60 }, { wpx: 60 }, { wpx: 60 },
|
|
{ wpx: 70 }, { wpx: 70 }, { wpx: 80 }, { wpx: 80 }, { wpx: 90 },
|
|
{ wpx: 60 }, { wpx: 60 },
|
|
{ wpx: 70 }, { wpx: 70 },
|
|
{ wpx: 80 },
|
|
{ wpx: 90 },
|
|
{ wpx: 60 }, { wpx: 60 },
|
|
];
|
|
|
|
const wb = XLSX.utils.book_new();
|
|
XLSX.utils.book_append_sheet(wb, ws, "sheet1");
|
|
XLSX.writeFile(wb, "확인매물_개인별실적_" + getDateTimeString() + ".xlsx");
|
|
}
|
|
|
|
// 엑셀 다운로드
|
|
function downloadExcel(data) {
|
|
const ws = XLSX.utils.json_to_sheet(data);
|
|
ws['!cols'] = [
|
|
{ wpx: 80 },
|
|
{ wpx: 80 },
|
|
{ wpx: 80 },
|
|
{ wpx: 80 },
|
|
{ wpx: 80 },
|
|
{ wpx: 80 },
|
|
{ wpx: 80 },
|
|
{ wpx: 80 },
|
|
{ wpx: 80 },
|
|
{ wpx: 80 },
|
|
{ wpx: 80 },
|
|
{ wpx: 80 },
|
|
{ wpx: 80 },
|
|
{ wpx: 80 },
|
|
{ wpx: 80 },
|
|
{ wpx: 80 },
|
|
];
|
|
|
|
const wb = XLSX.utils.book_new();
|
|
XLSX.utils.book_append_sheet(wb, ws, "Sheet1");
|
|
|
|
const wbout = XLSX.write(wb, { bookType: 'xlsx', type: 'array' });
|
|
|
|
const blob = new Blob([wbout], { type: 'application/octet-stream' });
|
|
|
|
const link = document.createElement("a");
|
|
link.href = URL.createObjectURL(blob);
|
|
link.download = "현장확인인원별배정현황" + getDateTimeString() + ".xlsx";
|
|
link.click();
|
|
URL.revokeObjectURL(link.href);
|
|
}
|
|
|
|
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() ?>
|