2025-01-16 05:53:43 +00:00
|
|
|
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
|
|
|
|
|
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
|
|
|
|
|
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
|
|
|
|
|
<%@ taglib prefix="ui" uri="http://egovframework.gov/ctl/ui"%>
|
|
|
|
|
<html>
|
2025-11-21 07:10:31 +00:00
|
|
|
<head>
|
|
|
|
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
|
|
|
|
|
|
|
|
|
<!-- Chart.js 라이브러리 -->
|
|
|
|
|
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
|
|
|
|
|
|
|
|
|
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css">
|
|
|
|
|
<link rel="stylesheet" HREF="${pageContext.request.contextPath}/css/admins/style.css" type="text/css">
|
|
|
|
|
|
|
|
|
|
<script>
|
|
|
|
|
var context = "${pageContext.request.contextPath}";
|
|
|
|
|
var myChartInstance = null; // 단계별 차트 인스턴스
|
|
|
|
|
var instChartInstance = null; // 기관별 차트 인스턴스 (신규)
|
|
|
|
|
|
|
|
|
|
// [1] 문서 로드 완료 시 초기 실행
|
|
|
|
|
document.addEventListener("DOMContentLoaded", function() {
|
|
|
|
|
// 초기 로딩 시 검색 조건 없이 전체 통계 조회
|
|
|
|
|
moveConstructionUserDetail();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// [2] 통계 조회 함수 (Vanilla JS)
|
|
|
|
|
function moveConstructionUserDetail() {
|
|
|
|
|
// 기본 검색 조건 (필요시 변경 가능)
|
|
|
|
|
var params = {
|
|
|
|
|
constTag: "Y"
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// AJAX 요청 (XMLHttpRequest 사용)
|
|
|
|
|
var xhr = new XMLHttpRequest();
|
|
|
|
|
xhr.open("POST", context + "/admins/constructionProjectManagement/selectStatistics.do", true);
|
|
|
|
|
xhr.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
|
|
|
|
|
|
|
|
|
|
xhr.onreadystatechange = function() {
|
|
|
|
|
if (xhr.readyState === 4) { // 요청 완료
|
|
|
|
|
if (xhr.status === 200) { // 성공
|
|
|
|
|
try {
|
|
|
|
|
var res = JSON.parse(xhr.responseText);
|
|
|
|
|
if(res.result == "true" || res.result == true) {
|
|
|
|
|
renderStatistics(res.data);
|
|
|
|
|
} else {
|
|
|
|
|
alert("통계 데이터를 불러오는 중 오류가 발생했습니다: " + (res.message || ""));
|
|
|
|
|
}
|
|
|
|
|
} catch (e) {
|
|
|
|
|
console.error("JSON 파싱 오류:", e);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
console.error("AJAX Error:", xhr.statusText);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
xhr.send(JSON.stringify(params));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// [3] 화면 갱신 함수
|
|
|
|
|
function renderStatistics(data) {
|
|
|
|
|
// (1) 전체 등록 수
|
|
|
|
|
var totalCountElem = document.getElementById("total-count");
|
|
|
|
|
if(totalCountElem) totalCountElem.textContent = (data.totalCount || 0) + " 건";
|
|
|
|
|
|
|
|
|
|
// (2) 지역별 통계
|
|
|
|
|
var busanElem = document.getElementById("busan-count");
|
|
|
|
|
var daeguElem = document.getElementById("daegu-count");
|
|
|
|
|
var sejongElem = document.getElementById("sejong-count");
|
|
|
|
|
|
|
|
|
|
if(busanElem) busanElem.textContent = "0 건";
|
|
|
|
|
if(daeguElem) daeguElem.textContent = "0 건";
|
|
|
|
|
if(sejongElem) sejongElem.textContent = "0 건";
|
|
|
|
|
|
|
|
|
|
// 서버에서 계산된 부산 카운트가 있으면 우선 적용
|
|
|
|
|
if(data.busanCount !== undefined && busanElem) {
|
|
|
|
|
busanElem.textContent = data.busanCount + " 건";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 기존 리스트 기반 지역 카운트 (필요 시 사용)
|
|
|
|
|
if(data.regionList) {
|
|
|
|
|
data.regionList.forEach(function(item) {
|
|
|
|
|
var region = item.regionName || item.REGION_NAME || "";
|
|
|
|
|
var count = item.cnt || item.CNT || 0;
|
|
|
|
|
|
|
|
|
|
// 부산은 위에서 처리했으므로 제외하거나 중복 처리 가능
|
|
|
|
|
if(region.indexOf("대구") >= 0 && daeguElem) daeguElem.textContent = count + " 건";
|
|
|
|
|
else if(region.indexOf("세종") >= 0 && sejongElem) sejongElem.textContent = count + " 건";
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// (3) 최근 입력된 건설현장
|
|
|
|
|
var recentArea = document.getElementById("recent-project-area");
|
|
|
|
|
var recentHtml = '<p class="fw-bold" style="font-size: 18px; color:#c87202;">최근 입력된 건설현장</p>';
|
|
|
|
|
|
|
|
|
|
if(data.recentList && data.recentList.length > 0) {
|
|
|
|
|
data.recentList.forEach(function(project) {
|
|
|
|
|
var name = project.constName || project.CONST_NAME || "";
|
|
|
|
|
var spot = project.projectStartSpot || project.PROJECT_START_SPOT || "";
|
|
|
|
|
var spotShort = spot.split(" ")[0];
|
|
|
|
|
recentHtml += '<p>' + name + ' - ' + spotShort + '</p>';
|
|
|
|
|
});
|
|
|
|
|
} else {
|
|
|
|
|
recentHtml += '<p>최근 등록된 데이터가 없습니다.</p>';
|
|
|
|
|
}
|
|
|
|
|
recentHtml += '<div class="d-flex justify-content-between align-items-center">' +
|
|
|
|
|
'<div class="btn-group"><button type="button" class="btn btn-sm btn-outline-secondary">+ 더 보기</button></div></div>';
|
|
|
|
|
if(recentArea) recentArea.innerHTML = recentHtml;
|
|
|
|
|
|
|
|
|
|
// (4) 단계별 건수 차트
|
|
|
|
|
updateStageChart(data.stageCounts || {});
|
|
|
|
|
|
|
|
|
|
// (5) [신규] 기관별 등록 건수 차트 업데이트
|
|
|
|
|
updateInstitutionChart(data.institutionStats || []);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 단계별 차트 그리기
|
|
|
|
|
function updateStageChart(stageCounts) {
|
|
|
|
|
var chartData = [
|
|
|
|
|
stageCounts.feasibility || 0,
|
|
|
|
|
stageCounts.basicDesign || 0,
|
|
|
|
|
stageCounts.detailDesign || 0,
|
|
|
|
|
stageCounts.construction || 0,
|
|
|
|
|
stageCounts.completion || 0,
|
|
|
|
|
stageCounts.maintenance || 0
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
var stageFeasElem = document.getElementById("stage-feasibility");
|
|
|
|
|
if(stageFeasElem) stageFeasElem.textContent = (stageCounts.feasibility || 0) + "건";
|
|
|
|
|
|
|
|
|
|
var ctx = document.getElementById('myChart');
|
|
|
|
|
if(!ctx) return;
|
|
|
|
|
|
|
|
|
|
if (myChartInstance) myChartInstance.destroy();
|
|
|
|
|
|
|
|
|
|
myChartInstance = new Chart(ctx, {
|
|
|
|
|
type: 'bar',
|
|
|
|
|
data: {
|
|
|
|
|
labels: ['타당성조사', '기본설계', '실시설계', '시공중', '준공', '유지보수'],
|
|
|
|
|
datasets: [{
|
|
|
|
|
label: '건설현장 단계별 건수',
|
|
|
|
|
data: chartData,
|
|
|
|
|
backgroundColor: 'rgba(54, 162, 235, 0.2)',
|
|
|
|
|
borderColor: 'rgba(54, 162, 235, 1)',
|
|
|
|
|
borderWidth: 1
|
|
|
|
|
}]
|
|
|
|
|
},
|
|
|
|
|
options: { scales: { y: { beginAtZero: true } } }
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// [신규] 기관별 차트 그리기 함수
|
|
|
|
|
function updateInstitutionChart(instStats) {
|
|
|
|
|
var ctx = document.getElementById('institutionChart');
|
|
|
|
|
if(!ctx) return;
|
|
|
|
|
|
|
|
|
|
if (instChartInstance) instChartInstance.destroy();
|
|
|
|
|
|
|
|
|
|
var labels = [];
|
|
|
|
|
var data = [];
|
|
|
|
|
|
|
|
|
|
// 데이터 분리 및 라벨 보정
|
|
|
|
|
instStats.forEach(function(stat) {
|
|
|
|
|
var name = stat.name;
|
|
|
|
|
|
|
|
|
|
// [라벨 보정] '울산광역시' -> '한국도로공사'
|
|
|
|
|
if (name === '울산광역시') {
|
|
|
|
|
name = '한국도로공사';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
labels.push(name);
|
|
|
|
|
data.push(stat.count);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
instChartInstance = new Chart(ctx, {
|
|
|
|
|
type: 'bar', // 막대 그래프
|
|
|
|
|
data: {
|
|
|
|
|
labels: labels,
|
|
|
|
|
datasets: [{
|
|
|
|
|
label: '등록 건수',
|
|
|
|
|
data: data,
|
|
|
|
|
backgroundColor: 'rgba(75, 192, 192, 0.5)', // 색상 설정
|
|
|
|
|
borderColor: 'rgba(75, 192, 192, 1)',
|
|
|
|
|
borderWidth: 1
|
|
|
|
|
}]
|
|
|
|
|
},
|
|
|
|
|
options: {
|
|
|
|
|
responsive: true,
|
|
|
|
|
maintainAspectRatio: false,
|
|
|
|
|
scales: {
|
|
|
|
|
y: {
|
|
|
|
|
beginAtZero: true,
|
|
|
|
|
ticks: { stepSize: 1 } // 정수 단위 표시
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
plugins: {
|
|
|
|
|
legend: { display: false }, // 범례 숨김 (단일 데이터셋이므로)
|
|
|
|
|
title: {
|
|
|
|
|
display: true,
|
|
|
|
|
text: '기관별 건설현장 등록 현황',
|
|
|
|
|
font: { size: 16 }
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
</script>
|
|
|
|
|
</head>
|
|
|
|
|
|
|
|
|
|
<body>
|
|
|
|
|
<h1>건설현장 통계</h1>
|
|
|
|
|
<div class="home-trainning">
|
|
|
|
|
<div class="container-fluid">
|
|
|
|
|
<div class="row content">
|
|
|
|
|
<div class="col-sm-12">
|
|
|
|
|
|
|
|
|
|
<!-- [신규] 기관별 통계 그래프 영역 추가 (하단 전체 너비) -->
|
|
|
|
|
<div class="row" style="margin-top: 30px;">
|
|
|
|
|
<div class="col-sm-12">
|
|
|
|
|
<div class="well" style="background-color: #fff;">
|
|
|
|
|
<p class="fw-bold" style="font-size: 18px; color:#c87202; margin-bottom: 15px;">기관별 등록 건수</p>
|
|
|
|
|
<div style="height: 400px;"> <!-- 높이 지정 -->
|
|
|
|
|
<canvas id="institutionChart"></canvas>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="row">
|
|
|
|
|
<!-- 최근 입력된 건설현장 -->
|
|
|
|
|
<div class="col-sm-4">
|
|
|
|
|
<div class="well" id="recent-project-area">
|
|
|
|
|
<p class="fw-bold" style="font-size: 18px; color:#c87202;">최근 입력된 건설현장</p>
|
|
|
|
|
<p>로딩 중...</p>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- 단계별 건수 텍스트 -->
|
|
|
|
|
<div class="col-sm-4">
|
|
|
|
|
<div class="well">
|
|
|
|
|
<p class="fw-bold" style="font-size: 18px; color:#c87202;">단계별 건수</p>
|
|
|
|
|
<p><b>타당성조사 및 계획검토:</b> <span id="stage-feasibility">0건</span></p>
|
|
|
|
|
<p><b>기본설계:</b> 0건</p>
|
|
|
|
|
<p><b>실시설계:</b> 0건</p>
|
|
|
|
|
<p><b>시공중:</b> 0건</p>
|
|
|
|
|
<p><b>준공:</b> 0건</p>
|
|
|
|
|
<p><b>유지보수:</b> 0건</p>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- 단계별 건수 그래프 -->
|
|
|
|
|
<div class="col-sm-4">
|
|
|
|
|
<div class="well">
|
|
|
|
|
<canvas id="myChart"></canvas>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</body>
|
|
|
|
|
</html>
|