실적관리 추가
This commit is contained in:
420
app/Views/pages/results/m415/stats.php
Normal file
420
app/Views/pages/results/m415/stats.php
Normal file
@@ -0,0 +1,420 @@
|
||||
<?= $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 rowspan="2">모바일검증</th>
|
||||
<th colspan="3">전화확인</th>
|
||||
<th colspan="3">홍보확인서 확인</th>
|
||||
<th colspan="3">신홍보확인서 확인</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>전화 검증</th>
|
||||
<th>등기부등본 검증</th>
|
||||
<th>총소요</th>
|
||||
<th>FAX검증</th>
|
||||
<th>등기부등본 검증</th>
|
||||
<th>총소요</th>
|
||||
<th>FAX검증</th>
|
||||
<th>등기부등본 검증</th>
|
||||
<th>총소요</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
</tbody>
|
||||
</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>
|
||||
<style>
|
||||
table.dataTable thead th {
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
<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: '/m415/m415a/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: 'stan_date' },
|
||||
{ data: 'm_tot' },
|
||||
{ data: 't_doc' },
|
||||
{ data: 't_cert' },
|
||||
{ data: 't_tot' },
|
||||
{ data: 'd_doc' },
|
||||
{ data: 'd_cert' },
|
||||
{ data: 'd_tot' },
|
||||
{ data: 'n_doc' },
|
||||
{ data: 'n_cert' },
|
||||
{ data: 'n_tot' },
|
||||
|
||||
],
|
||||
// 옵션들 예시
|
||||
paging: false,
|
||||
searching: false,
|
||||
ordering: false,
|
||||
serverSide: true,
|
||||
});
|
||||
|
||||
|
||||
|
||||
$('#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: "/m415/m415a/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) {
|
||||
|
||||
/* =======================
|
||||
* 0) 유틸
|
||||
* ======================= */
|
||||
const COLS = 11;
|
||||
|
||||
const fitCols = (arr) => {
|
||||
const a = (arr || []).slice(0, COLS);
|
||||
while (a.length < COLS) a.push("");
|
||||
return a;
|
||||
};
|
||||
|
||||
const safe = (v) => (v === undefined || v === null) ? "" : v;
|
||||
|
||||
// 숫자/빈값 안전 처리 (소요시간이 숫자라면 여기로)
|
||||
const num = (v) => (v === undefined || v === null || v === "") ? 0 : Number(v);
|
||||
|
||||
// (선택) 초(seconds) → hh:mm:ss 로 보여주고 싶으면 사용
|
||||
// const secToHms = (sec) => {
|
||||
// const s = Number(sec || 0);
|
||||
// const h = String(Math.floor(s / 3600)).padStart(2, "0");
|
||||
// const m = String(Math.floor((s % 3600) / 60)).padStart(2, "0");
|
||||
// const ss = String(Math.floor(s % 60)).padStart(2, "0");
|
||||
// return `${h}:${m}:${ss}`;
|
||||
// };
|
||||
|
||||
/* =======================
|
||||
* 1) 헤더 (thead와 동일)
|
||||
* ======================= */
|
||||
const header1 = fitCols([
|
||||
"일자",
|
||||
"모바일검증",
|
||||
"전화확인", "", "",
|
||||
"홍보확인서 확인", "", "",
|
||||
"신홍보확인서 확인", "", ""
|
||||
]);
|
||||
|
||||
const header2 = fitCols([
|
||||
"",
|
||||
"",
|
||||
"전화 검증",
|
||||
"등기부등본 검증",
|
||||
"총소요",
|
||||
"FAX검증",
|
||||
"등기부등본 검증",
|
||||
"총소요",
|
||||
"FAX검증",
|
||||
"등기부등본 검증",
|
||||
"총소요",
|
||||
]);
|
||||
|
||||
/* =======================
|
||||
* 2) 바디 (DataTables columns 순서 그대로)
|
||||
* columns:
|
||||
* stan_date, m_tot,
|
||||
* t_doc, t_cert, t_tot,
|
||||
* d_doc, d_cert, d_tot,
|
||||
* n_doc, n_cert, n_tot
|
||||
* ======================= */
|
||||
const body = (dataRows || []).map(r => fitCols([
|
||||
safe(r.stan_date),
|
||||
safe(r.m_tot),
|
||||
|
||||
safe(r.t_doc),
|
||||
safe(r.t_cert),
|
||||
safe(r.t_tot), // 숫자면 num(r.t_tot)
|
||||
|
||||
safe(r.d_doc),
|
||||
safe(r.d_cert),
|
||||
safe(r.d_tot),
|
||||
|
||||
safe(r.n_doc),
|
||||
safe(r.n_cert),
|
||||
safe(r.n_tot),
|
||||
]));
|
||||
|
||||
const aoa = [header1, 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 } },
|
||||
// "모바일검증" rowspan 2
|
||||
{ s: { r: 0, c: 1 }, e: { r: 1, c: 1 } },
|
||||
|
||||
// "전화확인" colspan 3 (2~4)
|
||||
{ s: { r: 0, c: 2 }, e: { r: 0, c: 4 } },
|
||||
// "홍보확인서 확인" colspan 3 (5~7)
|
||||
{ s: { r: 0, c: 5 }, e: { r: 0, c: 7 } },
|
||||
// "신홍보확인서 확인" colspan 3 (8~10)
|
||||
{ s: { r: 0, c: 8 }, e: { r: 0, c: 10 } },
|
||||
];
|
||||
|
||||
/* =======================
|
||||
* 4) 컬럼 너비
|
||||
* ======================= */
|
||||
ws["!cols"] = [
|
||||
{ wpx: 100 }, // 일자
|
||||
{ wpx: 100 }, // 모바일검증
|
||||
{ wpx: 100 }, // 전화 검증
|
||||
{ wpx: 100 }, // 전화-등기부등본
|
||||
{ wpx: 100 }, // 전화-총소요
|
||||
{ wpx: 100 }, // 홍보 FAX
|
||||
{ wpx: 100 }, // 홍보-등기부등본
|
||||
{ wpx: 100 }, // 홍보-총소요
|
||||
{ wpx: 100 }, // 신홍보 FAX
|
||||
{ wpx: 100 }, // 신홍보-등기부등본
|
||||
{ wpx: 100 }, // 신홍보-총소요
|
||||
];
|
||||
|
||||
/* =======================
|
||||
* 5) (선택) 헤더 스타일 + 테두리 (xlsx-js-style 사용 시)
|
||||
* ======================= */
|
||||
const lastRow = aoa.length - 1;
|
||||
const lastCol = COLS - 1;
|
||||
|
||||
const headerStyle = {
|
||||
font: { bold: true },
|
||||
alignment: { horizontal: "center", vertical: "center" },
|
||||
fill: { patternType: "solid", fgColor: { rgb: "F2F2F2" } },
|
||||
border: {
|
||||
top: { style: "thin", color: { rgb: "D9D9D9" } },
|
||||
bottom: { style: "thin", color: { rgb: "D9D9D9" } },
|
||||
left: { style: "thin", color: { rgb: "D9D9D9" } },
|
||||
right: { style: "thin", color: { rgb: "D9D9D9" } },
|
||||
}
|
||||
};
|
||||
|
||||
const cellBorder = {
|
||||
border: {
|
||||
top: { style: "thin", color: { rgb: "E0E0E0" } },
|
||||
bottom: { style: "thin", color: { rgb: "E0E0E0" } },
|
||||
left: { style: "thin", color: { rgb: "E0E0E0" } },
|
||||
right: { style: "thin", color: { rgb: "E0E0E0" } },
|
||||
}
|
||||
};
|
||||
|
||||
// 모든 셀 테두리 + 헤더 스타일
|
||||
for (let r = 0; r <= lastRow; r++) {
|
||||
for (let c = 0; c <= lastCol; c++) {
|
||||
const addr = XLSX.utils.encode_cell({ r, c });
|
||||
if (!ws[addr]) ws[addr] = { t: "s", v: "" };
|
||||
|
||||
// 기본 테두리
|
||||
ws[addr].s = Object.assign({}, ws[addr].s || {}, cellBorder);
|
||||
|
||||
// 헤더(0~1행)는 헤더 스타일 덮어쓰기
|
||||
if (r <= 1) ws[addr].s = Object.assign({}, ws[addr].s, headerStyle);
|
||||
// 바디 가운데 정렬 원하면:
|
||||
// else ws[addr].s = Object.assign({}, ws[addr].s, { alignment:{horizontal:"center",vertical:"center"}});
|
||||
}
|
||||
}
|
||||
|
||||
// 헤더 높이(보기 좋게)
|
||||
ws["!rows"] = [{ hpx: 24 }, { hpx: 24 }];
|
||||
|
||||
/* =======================
|
||||
* 6) 저장
|
||||
* ======================= */
|
||||
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() ?>
|
||||
Reference in New Issue
Block a user