feat: 건설현장 관리에서 건설현장 통계에 발주기관 별 건설현장 프로젝트 입력상태 및 기관별 성과 현황 구헌
parent
9e1186280e
commit
bc5d1622a6
|
|
@ -45,4 +45,14 @@ public interface ConstructionProjectManagementMapper {
|
|||
*/
|
||||
public int selectSiteCountByDistrictCodes(HashMap<String, Object> params) throws SQLException;
|
||||
|
||||
/**
|
||||
* [신규] 기관별 입력 상태별(PROJECT_STATE_CODE) 현황 조회
|
||||
*/
|
||||
public List<EgovMap> selectProjectStateCountByDistrictCodes(HashMap<String, Object> params) throws SQLException;
|
||||
|
||||
/**
|
||||
* [신규] 기관별 성과 현황(CONST_STATE_CODE - 공사단계) 조회
|
||||
*/
|
||||
public List<EgovMap> selectConstStateCountByDistrictCodes(HashMap<String, Object> params) throws SQLException;
|
||||
|
||||
}
|
||||
|
|
@ -28,14 +28,11 @@ public class ConstructionProjectManagementServiceImpl implements ConstructionPro
|
|||
@Autowired
|
||||
private DrillingInputService drillingInputService;
|
||||
|
||||
/**
|
||||
* 건설현장 통계 대시보드 데이터 조회
|
||||
*/
|
||||
@Override
|
||||
public Map<String, Object> selectConstructionProjectStatistics(HashMap<String, Object> params) throws Exception {
|
||||
Map<String, Object> resultMap = new HashMap<>();
|
||||
|
||||
// 1. 사용자 권한 및 조직 코드 설정 (기존 로직)
|
||||
// 1. 사용자 권한 및 조직 코드 설정
|
||||
String userId = MyUtil.getStringFromObject(params.get("userId"));
|
||||
if (userId != null && !userId.isEmpty()) {
|
||||
HashMap<String, Object> orgCodes = drillingInputService.getOrganizationUserGlGmGsGfCodes(userId);
|
||||
|
|
@ -53,99 +50,129 @@ public class ConstructionProjectManagementServiceImpl implements ConstructionPro
|
|||
resultMap.put("totalCount", totalCount);
|
||||
|
||||
// =================================================================
|
||||
// [기관별 통계 로직] v_gl='01' 필터링 및 코드순 정렬
|
||||
// [기관별 통계 로직] 데이터 합산 및 정렬
|
||||
// =================================================================
|
||||
List<Map<String, Object>> institutionStats = new ArrayList<>();
|
||||
// 중복 기관 합산을 위한 맵 (Key: 기관명)
|
||||
Map<String, Map<String, Object>> uniqueStatsMap = new HashMap<>();
|
||||
|
||||
// A. 통계 대상 기관명 목록 조회
|
||||
List<String> companyList = constructionProjectManagementMapper.selectStatTargetCompanies();
|
||||
|
||||
// 부산 지역(v_gl='01') 합계용 변수
|
||||
int busanTotalCount = 0;
|
||||
|
||||
if (companyList != null) {
|
||||
for (String companyName : companyList) {
|
||||
if (companyName == null || companyName.trim().isEmpty()) continue;
|
||||
|
||||
// B. SP 호출하여 지역코드(GL, GM, GS) 획득
|
||||
HashMap<String, Object> spParams = new HashMap<>();
|
||||
spParams.put("projectMasterCompanyName", companyName);
|
||||
|
||||
// SP 실행 (OUT 변수는 spParams에 담김: v_gl, v_gm, v_gs)
|
||||
constructionProjectManagementMapper.spGetMasterCompanyDistrict(spParams);
|
||||
|
||||
String v_gl = (String) spParams.get("v_gl");
|
||||
String v_gm = (String) spParams.get("v_gm");
|
||||
String v_gs = (String) spParams.get("v_gs");
|
||||
|
||||
// [필터링 조건] v_gl이 '01'인 경우에만 처리
|
||||
// [필터링] v_gl이 '01'인 경우에만 처리
|
||||
if ("01".equals(v_gl)) {
|
||||
|
||||
// C. 해당 지역코드로 건설현장 수 카운트
|
||||
HashMap<String, Object> countParams = new HashMap<>();
|
||||
countParams.put("v_gl", v_gl);
|
||||
countParams.put("v_gm", v_gm);
|
||||
countParams.put("v_gs", v_gs);
|
||||
// [명칭 보정] 한국도로공사 코드인 경우 이름을 강제 통일 (울산광역시 -> 한국도로공사 등)
|
||||
if ("01".equals(v_gl) && "02".equals(v_gm) && "002".equals(v_gs)) {
|
||||
companyName = "한국도로공사";
|
||||
}
|
||||
|
||||
int siteCount = constructionProjectManagementMapper.selectSiteCountByDistrictCodes(countParams);
|
||||
HashMap<String, Object> queryParams = new HashMap<>();
|
||||
queryParams.put("v_gl", v_gl);
|
||||
queryParams.put("v_gm", v_gm);
|
||||
queryParams.put("v_gs", v_gs);
|
||||
|
||||
// 1) 전체 건수
|
||||
int siteCount = constructionProjectManagementMapper.selectSiteCountByDistrictCodes(queryParams);
|
||||
|
||||
// 그래프용 데이터 저장 (기관명, 건수, 정렬용 코드들)
|
||||
Map<String, Object> stat = new HashMap<>();
|
||||
stat.put("name", companyName);
|
||||
stat.put("count", siteCount);
|
||||
stat.put("gl", v_gl);
|
||||
stat.put("gm", v_gm);
|
||||
stat.put("gs", v_gs);
|
||||
// 2) 입력 상태별 현황
|
||||
List<EgovMap> inputStateList = constructionProjectManagementMapper.selectProjectStateCountByDistrictCodes(queryParams);
|
||||
int[] inputStatusData = new int[7];
|
||||
for(EgovMap m : inputStateList) {
|
||||
int code = MyUtil.getIntegerFromObject(m.get("code"));
|
||||
int cnt = MyUtil.getIntegerFromObject(m.get("cnt"));
|
||||
if(code >= 0 && code <= 6) inputStatusData[code] = cnt;
|
||||
}
|
||||
|
||||
// 3) 성과 현황
|
||||
List<EgovMap> perfStateList = constructionProjectManagementMapper.selectConstStateCountByDistrictCodes(queryParams);
|
||||
int[] performanceData = new int[6];
|
||||
for(EgovMap m : perfStateList) {
|
||||
int code = MyUtil.getIntegerFromObject(m.get("code"));
|
||||
int cnt = MyUtil.getIntegerFromObject(m.get("cnt"));
|
||||
if(code >= 1 && code <= 6) performanceData[code - 1] = cnt;
|
||||
}
|
||||
|
||||
institutionStats.add(stat);
|
||||
// [데이터 합산 로직] 이미 존재하는 기관명이면 데이터 누적
|
||||
if (uniqueStatsMap.containsKey(companyName)) {
|
||||
Map<String, Object> existing = uniqueStatsMap.get(companyName);
|
||||
|
||||
// 건수 합산
|
||||
int currentCount = (Integer) existing.get("count");
|
||||
existing.put("count", currentCount + siteCount);
|
||||
|
||||
// 입력상태 배열 합산
|
||||
int[] exInput = (int[]) existing.get("inputStatusData");
|
||||
for(int i=0; i<7; i++) exInput[i] += inputStatusData[i];
|
||||
existing.put("inputStatusData", exInput);
|
||||
|
||||
// 성과현황 배열 합산
|
||||
int[] exPerf = (int[]) existing.get("performanceData");
|
||||
for(int i=0; i<6; i++) exPerf[i] += performanceData[i];
|
||||
existing.put("performanceData", exPerf);
|
||||
|
||||
} else {
|
||||
// 신규 추가
|
||||
Map<String, Object> stat = new HashMap<>();
|
||||
stat.put("name", companyName);
|
||||
stat.put("count", siteCount);
|
||||
stat.put("gl", v_gl);
|
||||
stat.put("gm", v_gm);
|
||||
stat.put("gs", v_gs);
|
||||
stat.put("inputStatusData", inputStatusData);
|
||||
stat.put("performanceData", performanceData);
|
||||
|
||||
uniqueStatsMap.put(companyName, stat);
|
||||
}
|
||||
|
||||
// 부산 지역 합계 누적
|
||||
busanTotalCount += siteCount;
|
||||
}
|
||||
}
|
||||
|
||||
// [정렬] GL_CODE ASC, GM_CODE ASC, GS_CODE ASC 순서로 정렬
|
||||
Collections.sort(institutionStats, new Comparator<Map<String, Object>>() {
|
||||
@Override
|
||||
public int compare(Map<String, Object> o1, Map<String, Object> o2) {
|
||||
String gl1 = (String) o1.get("gl");
|
||||
String gl2 = (String) o2.get("gl");
|
||||
int result = compareString(gl1, gl2);
|
||||
if (result != 0) return result;
|
||||
|
||||
String gm1 = (String) o1.get("gm");
|
||||
String gm2 = (String) o2.get("gm");
|
||||
result = compareString(gm1, gm2);
|
||||
if (result != 0) return result;
|
||||
|
||||
String gs1 = (String) o1.get("gs");
|
||||
String gs2 = (String) o2.get("gs");
|
||||
return compareString(gs1, gs2);
|
||||
}
|
||||
|
||||
// null-safe string comparison
|
||||
private int compareString(String s1, String s2) {
|
||||
if (s1 == null) s1 = "";
|
||||
if (s2 == null) s2 = "";
|
||||
return s1.compareTo(s2);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 결과 맵에 저장 (정렬된 리스트)
|
||||
// Map 값을 List로 변환
|
||||
List<Map<String, Object>> institutionStats = new ArrayList<>(uniqueStatsMap.values());
|
||||
|
||||
// [정렬] GL_CODE ASC, GM_CODE ASC, GS_CODE ASC 순서로 정렬
|
||||
Collections.sort(institutionStats, new Comparator<Map<String, Object>>() {
|
||||
@Override
|
||||
public int compare(Map<String, Object> o1, Map<String, Object> o2) {
|
||||
String gl1 = (String) o1.get("gl"); String gl2 = (String) o2.get("gl");
|
||||
int result = compareString(gl1, gl2); if (result != 0) return result;
|
||||
|
||||
String gm1 = (String) o1.get("gm"); String gm2 = (String) o2.get("gm");
|
||||
result = compareString(gm1, gm2); if (result != 0) return result;
|
||||
|
||||
String gs1 = (String) o1.get("gs"); String gs2 = (String) o2.get("gs");
|
||||
return compareString(gs1, gs2);
|
||||
}
|
||||
private int compareString(String s1, String s2) {
|
||||
return (s1 == null ? "" : s1).compareTo(s2 == null ? "" : s2);
|
||||
}
|
||||
});
|
||||
|
||||
resultMap.put("institutionStats", institutionStats);
|
||||
resultMap.put("busanCount", busanTotalCount);
|
||||
|
||||
|
||||
// 3. 지역별 통계 조회
|
||||
List<EgovMap> regionList = constructionProjectManagementMapper.selectRegionCount(params);
|
||||
resultMap.put("regionList", regionList);
|
||||
|
||||
// 4. 단계별 통계 조회
|
||||
// 4. 단계별 통계
|
||||
List<EgovMap> stageList = constructionProjectManagementMapper.selectStageCount(params);
|
||||
Map<String, Integer> stageCounts = new HashMap<>();
|
||||
|
||||
// 초기화
|
||||
stageCounts.put("feasibility", 0);
|
||||
stageCounts.put("basicDesign", 0);
|
||||
stageCounts.put("detailDesign", 0);
|
||||
|
|
@ -169,7 +196,6 @@ public class ConstructionProjectManagementServiceImpl implements ConstructionPro
|
|||
// 5. 최근 입력된 건설현장
|
||||
params.put("firstIndex", 0);
|
||||
params.put("recordCountPerPage", 5);
|
||||
|
||||
List<EgovMap> recentList = constructionProjectManagementMapper.selectRecentProjects(params);
|
||||
resultMap.put("recentList", recentList);
|
||||
|
||||
|
|
@ -180,5 +206,4 @@ public class ConstructionProjectManagementServiceImpl implements ConstructionPro
|
|||
|
||||
return resultMap;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,10 +1,9 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
|
||||
<!-- namespace 확인: Java Interface 경로와 일치 -->
|
||||
<mapper namespace="geoinfo.admins.constructionProjectManagement.service.ConstructionProjectManagementMapper">
|
||||
|
||||
<!-- [공통] 검색 조건 Fragment (기존 유지) -->
|
||||
<!-- [공통] 검색 조건 Fragment -->
|
||||
<sql id="searchConditions">
|
||||
<if test="constName != null and constName != ''">
|
||||
AND A.CONST_NAME LIKE '%' || #{constName} || '%'
|
||||
|
|
@ -26,7 +25,7 @@
|
|||
</if>
|
||||
</sql>
|
||||
|
||||
<!-- 1. 전체 등록 수 조회 (기존 유지) -->
|
||||
<!-- 1. 전체 등록 수 조회 -->
|
||||
<select id="selectTotalCount" parameterType="hashmap" resultType="long">
|
||||
SELECT COUNT(*)
|
||||
FROM TEMP_CONSTRUCT_SITE_INFO A
|
||||
|
|
@ -34,7 +33,7 @@
|
|||
<include refid="searchConditions" />
|
||||
</select>
|
||||
|
||||
<!-- 2. 지역별 통계 조회 (기존 유지) -->
|
||||
<!-- 2. 지역별 통계 조회 -->
|
||||
<select id="selectRegionCount" parameterType="hashmap" resultType="egovMap">
|
||||
SELECT
|
||||
SUBSTR(B.PROJECT_START_SPOT, 1, 2) AS REGION_NAME,
|
||||
|
|
@ -47,7 +46,7 @@
|
|||
GROUP BY SUBSTR(B.PROJECT_START_SPOT, 1, 2)
|
||||
</select>
|
||||
|
||||
<!-- 3. 단계별 통계 조회 (기존 유지) -->
|
||||
<!-- 3. 단계별 통계 조회 (전체) -->
|
||||
<select id="selectStageCount" parameterType="hashmap" resultType="egovMap">
|
||||
SELECT
|
||||
CONST_STATE_CODE AS "constStateCode",
|
||||
|
|
@ -58,7 +57,7 @@
|
|||
GROUP BY CONST_STATE_CODE
|
||||
</select>
|
||||
|
||||
<!-- 4. 최근 입력된 건설현장 목록 조회 (기존 유지) -->
|
||||
<!-- 4. 최근 입력된 건설현장 목록 조회 -->
|
||||
<select id="selectRecentProjects" parameterType="hashmap" resultType="egovMap">
|
||||
SELECT *
|
||||
FROM (
|
||||
|
|
@ -83,7 +82,7 @@
|
|||
WHERE RN BETWEEN #{firstIndex} + 1 AND #{firstIndex} + #{recordCountPerPage}
|
||||
</select>
|
||||
|
||||
<!-- 5. [수정] 통계 대상 기관명 목록 조회 (사용자 요청 쿼리 적용) -->
|
||||
<!-- 5. 통계 대상 기관명 목록 조회 -->
|
||||
<select id="selectStatTargetCompanies" resultType="string">
|
||||
SELECT tmc.COM_NAME
|
||||
FROM (
|
||||
|
|
@ -112,7 +111,7 @@
|
|||
)}
|
||||
</select>
|
||||
|
||||
<!-- 7. [수정] 지역코드로 건설현장 수 구하기 (사용자 요청 매핑 적용) -->
|
||||
<!-- 7. 지역코드로 건설현장 수 구하기 -->
|
||||
<select id="selectSiteCountByDistrictCodes" parameterType="hashmap" resultType="int">
|
||||
SELECT COUNT(*)
|
||||
FROM TEMP_CONSTRUCT_SITE_INFO
|
||||
|
|
@ -123,4 +122,30 @@
|
|||
<!-- AND CONST_TAG = 'Y' -->
|
||||
</select>
|
||||
|
||||
<!-- [수정] 8. 기관별 입력 상태별(PROJECT_STATE_CODE) 현황 조회 -->
|
||||
<!-- CONST_TAG = 'Y' 조건 제거 -->
|
||||
<select id="selectProjectStateCountByDistrictCodes" parameterType="hashmap" resultType="egovMap">
|
||||
SELECT
|
||||
PROJECT_STATE_CODE AS "code",
|
||||
COUNT(*) AS "cnt"
|
||||
FROM TEMP_CONSTRUCT_SITE_INFO
|
||||
WHERE MASTER_COMPANY_O_CODE = #{v_gl}
|
||||
AND MASTER_COMPANY_TW_CODE = #{v_gm}
|
||||
AND MASTER_COMPANY_TH_CODE = #{v_gs}
|
||||
GROUP BY PROJECT_STATE_CODE
|
||||
</select>
|
||||
|
||||
<!-- [수정] 9. 기관별 성과 현황(CONST_STATE_CODE) 조회 -->
|
||||
<!-- CONST_TAG = 'Y' 조건 제거 -->
|
||||
<select id="selectConstStateCountByDistrictCodes" parameterType="hashmap" resultType="egovMap">
|
||||
SELECT
|
||||
CONST_STATE_CODE AS "code",
|
||||
COUNT(*) AS "cnt"
|
||||
FROM TEMP_CONSTRUCT_SITE_INFO
|
||||
WHERE MASTER_COMPANY_O_CODE = #{v_gl}
|
||||
AND MASTER_COMPANY_TW_CODE = #{v_gm}
|
||||
AND MASTER_COMPANY_TH_CODE = #{v_gs}
|
||||
GROUP BY CONST_STATE_CODE
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
|
|
@ -12,32 +12,56 @@
|
|||
<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">
|
||||
|
||||
<style>
|
||||
/* 그래프 컨테이너 스타일 */
|
||||
.chart-wrapper {
|
||||
border: 1px solid #ddd;
|
||||
padding: 20px;
|
||||
margin-bottom: 30px;
|
||||
background: #fff;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.05);
|
||||
}
|
||||
.chart-header {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
margin-bottom: 20px;
|
||||
padding-bottom: 10px;
|
||||
border-bottom: 2px solid #c87202;
|
||||
}
|
||||
.sub-chart-title {
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
margin-bottom: 10px;
|
||||
color: #555;
|
||||
}
|
||||
.sub-chart-box {
|
||||
height: 300px;
|
||||
position: relative;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
var context = "${pageContext.request.contextPath}";
|
||||
var myChartInstance = null; // 단계별 차트 인스턴스
|
||||
var instChartInstance = null; // 기관별 차트 인스턴스 (신규)
|
||||
var myChartInstance = null; // 상단 전체 단계별 차트
|
||||
var instChartInstance = null; // 하단 전체 기관별 차트
|
||||
|
||||
// [1] 문서 로드 완료 시 초기 실행
|
||||
// [1] 문서 로드 완료 시 초기 실행 (Vanilla JS)
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
// 초기 로딩 시 검색 조건 없이 전체 통계 조회
|
||||
moveConstructionUserDetail();
|
||||
});
|
||||
|
||||
// [2] 통계 조회 함수 (Vanilla JS)
|
||||
function moveConstructionUserDetail() {
|
||||
// 기본 검색 조건 (필요시 변경 가능)
|
||||
var params = {
|
||||
constTag: "Y"
|
||||
};
|
||||
|
||||
// AJAX 요청 (XMLHttpRequest 사용)
|
||||
var params = { constTag: "Y" };
|
||||
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) { // 성공
|
||||
if (xhr.readyState === 4) {
|
||||
if (xhr.status === 200) {
|
||||
try {
|
||||
var res = JSON.parse(xhr.responseText);
|
||||
if(res.result == "true" || res.result == true) {
|
||||
|
|
@ -56,64 +80,28 @@
|
|||
xhr.send(JSON.stringify(params));
|
||||
}
|
||||
|
||||
// [3] 화면 갱신 함수
|
||||
// [3] 화면 갱신 메인 함수
|
||||
function renderStatistics(data) {
|
||||
// (1) 전체 등록 수
|
||||
var totalCountElem = document.getElementById("total-count");
|
||||
if(totalCountElem) totalCountElem.textContent = (data.totalCount || 0) + " 건";
|
||||
|
||||
// (2) 지역별 통계
|
||||
// (1) 상단 요약 수치 갱신 (Vanilla JS)
|
||||
var totalElem = document.getElementById("total-count");
|
||||
if (totalElem) totalElem.textContent = (data.totalCount || 0) + " 건";
|
||||
|
||||
var busanElem = document.getElementById("busan-count");
|
||||
var daeguElem = document.getElementById("daegu-count");
|
||||
var sejongElem = document.getElementById("sejong-count");
|
||||
if (data.busanCount !== undefined && busanElem) {
|
||||
busanElem.textContent = data.busanCount + " 건";
|
||||
}
|
||||
|
||||
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) 단계별 건수 차트
|
||||
// (2) 상단 전체 단계별 차트 갱신
|
||||
updateStageChart(data.stageCounts || {});
|
||||
|
||||
// (3) [신규] 기관별 상세 그래프 동적 생성 (입력상태, 성과현황)
|
||||
createInstitutionDetailCharts(data.institutionStats || []);
|
||||
|
||||
// (5) [신규] 기관별 등록 건수 차트 업데이트
|
||||
// (4) 하단 전체 기관 등록 건수 차트 갱신
|
||||
updateInstitutionChart(data.institutionStats || []);
|
||||
}
|
||||
|
||||
// 단계별 차트 그리기
|
||||
// [상단] 전체 단계별 차트
|
||||
function updateStageChart(stageCounts) {
|
||||
var chartData = [
|
||||
stageCounts.feasibility || 0,
|
||||
|
|
@ -123,16 +111,17 @@
|
|||
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;
|
||||
// 텍스트 업데이트 (Vanilla JS)
|
||||
var stageElem = document.getElementById("stage-feasibility");
|
||||
if (stageElem) stageElem.textContent = (stageCounts.feasibility || 0) + "건";
|
||||
|
||||
var canvas = document.getElementById('myChart');
|
||||
if(!canvas) return;
|
||||
|
||||
if (myChartInstance) myChartInstance.destroy();
|
||||
|
||||
myChartInstance = new Chart(ctx, {
|
||||
myChartInstance = new Chart(canvas, {
|
||||
type: 'bar',
|
||||
data: {
|
||||
labels: ['타당성조사', '기본설계', '실시설계', '시공중', '준공', '유지보수'],
|
||||
|
|
@ -148,38 +137,97 @@
|
|||
});
|
||||
}
|
||||
|
||||
// [신규] 기관별 차트 그리기 함수
|
||||
function updateInstitutionChart(instStats) {
|
||||
var ctx = document.getElementById('institutionChart');
|
||||
if(!ctx) return;
|
||||
|
||||
if (instChartInstance) instChartInstance.destroy();
|
||||
|
||||
var labels = [];
|
||||
var data = [];
|
||||
// [중단] 기관별 상세 차트 동적 생성 함수
|
||||
function createInstitutionDetailCharts(instStats) {
|
||||
var container = document.getElementById("institution-detail-area");
|
||||
if (!container) return;
|
||||
|
||||
// 데이터 분리 및 라벨 보정
|
||||
instStats.forEach(function(stat) {
|
||||
var name = stat.name;
|
||||
|
||||
// [라벨 보정] '울산광역시' -> '한국도로공사'
|
||||
if (name === '울산광역시') {
|
||||
name = '한국도로공사';
|
||||
}
|
||||
|
||||
labels.push(name);
|
||||
data.push(stat.count);
|
||||
});
|
||||
container.innerHTML = ""; // 기존 내용 초기화
|
||||
|
||||
instChartInstance = new Chart(ctx, {
|
||||
type: 'bar', // 막대 그래프
|
||||
instStats.forEach(function(stat, index) {
|
||||
// 라벨 보정
|
||||
var name = stat.name;
|
||||
if (name === '울산광역시') name = '한국도로공사';
|
||||
|
||||
// Canvas ID 생성
|
||||
var inputChartId = 'inputChart_' + index;
|
||||
var perfChartId = 'perfChart_' + index;
|
||||
|
||||
// HTML 구조 생성
|
||||
var html = '';
|
||||
html += '<div class="col-sm-12">';
|
||||
html += ' <div class="chart-wrapper">';
|
||||
html += ' <div class="chart-header">' + name + ' (총 ' + stat.count + '건)</div>';
|
||||
html += ' <div class="row">';
|
||||
html += ' <div class="col-sm-6">';
|
||||
html += ' <div class="sub-chart-title">건설현장 프로젝트 입력상태</div>';
|
||||
html += ' <div class="sub-chart-box"><canvas id="' + inputChartId + '"></canvas></div>';
|
||||
html += ' </div>';
|
||||
html += ' <div class="col-sm-6">';
|
||||
html += ' <div class="sub-chart-title">기관별 성과 현황</div>';
|
||||
html += ' <div class="sub-chart-box"><canvas id="' + perfChartId + '"></canvas></div>';
|
||||
html += ' </div>';
|
||||
html += ' </div>';
|
||||
html += ' </div>';
|
||||
html += '</div>';
|
||||
|
||||
// DOM에 추가
|
||||
var div = document.createElement("div");
|
||||
div.className = "row"; // 행 단위로 추가
|
||||
div.innerHTML = html;
|
||||
container.appendChild(div);
|
||||
|
||||
// 1) 입력상태 파이 차트 그리기
|
||||
drawPieChart(inputChartId, stat.inputStatusData);
|
||||
|
||||
// 2) 성과현황 막대 차트 그리기
|
||||
drawBarChart(perfChartId, stat.performanceData);
|
||||
});
|
||||
}
|
||||
|
||||
function drawPieChart(id, dataArr) {
|
||||
var canvas = document.getElementById(id);
|
||||
if(!canvas) return;
|
||||
|
||||
new Chart(canvas, {
|
||||
type: 'pie',
|
||||
data: {
|
||||
labels: labels,
|
||||
labels: ['미입력', '입력중', '검수대기', '검수중', '수정요청', '검수완료', '등록완료'],
|
||||
datasets: [{
|
||||
label: '등록 건수',
|
||||
data: data,
|
||||
backgroundColor: 'rgba(75, 192, 192, 0.5)', // 색상 설정
|
||||
borderColor: 'rgba(75, 192, 192, 1)',
|
||||
data: dataArr,
|
||||
backgroundColor: [
|
||||
'#e0e0e0', // 미입력 (회색)
|
||||
'#ffcd56', // 입력중 (노랑)
|
||||
'#36a2eb', // 검수대기 (파랑)
|
||||
'#ff6384', // 검수중 (빨강)
|
||||
'#ff9f40', // 수정요청 (주황)
|
||||
'#4bc0c0', // 검수완료 (청록)
|
||||
'#9966ff' // 등록완료 (보라)
|
||||
]
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
plugins: {
|
||||
legend: { position: 'right' }
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function drawBarChart(id, dataArr) {
|
||||
var canvas = document.getElementById(id);
|
||||
if(!canvas) return;
|
||||
|
||||
new Chart(canvas, {
|
||||
type: 'bar',
|
||||
data: {
|
||||
labels: ['타당성조사', '기본설계', '실시설계', '시공중', '준공', '유지보수'],
|
||||
datasets: [{
|
||||
label: '건수',
|
||||
data: dataArr,
|
||||
backgroundColor: '#36a2eb',
|
||||
borderWidth: 1
|
||||
}]
|
||||
},
|
||||
|
|
@ -187,18 +235,51 @@
|
|||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
scales: {
|
||||
y: {
|
||||
beginAtZero: true,
|
||||
ticks: { stepSize: 1 } // 정수 단위 표시
|
||||
}
|
||||
y: { beginAtZero: true, ticks: { stepSize: 1 } }
|
||||
},
|
||||
plugins: {
|
||||
legend: { display: false }, // 범례 숨김 (단일 데이터셋이므로)
|
||||
title: {
|
||||
display: true,
|
||||
text: '기관별 건설현장 등록 현황',
|
||||
font: { size: 16 }
|
||||
}
|
||||
legend: { display: false }
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// [하단] 전체 기관별 등록 건수 차트
|
||||
function updateInstitutionChart(instStats) {
|
||||
var canvas = document.getElementById('institutionChart');
|
||||
if(!canvas) 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(canvas, {
|
||||
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: '전체 기관별 등록 현황' }
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
@ -211,48 +292,24 @@
|
|||
<div class="home-trainning">
|
||||
<div class="container-fluid">
|
||||
<div class="row content">
|
||||
<div class="col-sm-12">
|
||||
|
||||
<!-- [신규] 기관별 통계 그래프 영역 추가 (하단 전체 너비) -->
|
||||
<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;"> <!-- 높이 지정 -->
|
||||
<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 id="institution-detail-area" style="margin-top: 30px;">
|
||||
<!-- 여기에 Javascript로 기관별 그래프들이 추가됨 -->
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Reference in New Issue