feat: 발주기관별시추정보관리사이트기능개선(FUR-001) - 발주처입력시자동완성기능구현

main
thkim 2025-08-11 16:32:15 +09:00
parent e6bfd33c93
commit ff8f9f6fcb
5 changed files with 296 additions and 88 deletions

View File

@ -5,6 +5,7 @@ import geoinfo.drilling.input.service.DrillingInputService;
import geoinfo.drilling.inquiry.service.DrillingInquiryMapper; import geoinfo.drilling.inquiry.service.DrillingInquiryMapper;
import geoinfo.drilling.inquiry.service.DrillingInquiryService; import geoinfo.drilling.inquiry.service.DrillingInquiryService;
import geoinfo.main.login.service.LoginMapper; import geoinfo.main.login.service.LoginMapper;
import geoinfo.main.login.service.LoginService;
import geoinfo.util.MyUtil; import geoinfo.util.MyUtil;
import java.sql.SQLException; import java.sql.SQLException;
@ -13,6 +14,7 @@ import java.text.SimpleDateFormat;
import java.util.Date; import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import javax.annotation.Resource; import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
@ -38,6 +40,10 @@ public class DrillingInquiryServiceImpl implements DrillingInquiryService {
@Autowired @Autowired
DrillingInputService drillingInputService; DrillingInputService drillingInputService;
@Resource(name = "loginService")
private LoginService loginService;
@Resource(name="loginMapper") @Resource(name="loginMapper")
private LoginMapper loginMapper; private LoginMapper loginMapper;
@ -345,6 +351,30 @@ public class DrillingInquiryServiceImpl implements DrillingInquiryService {
throw new Exception( "로그인이 필요한 서비스입니다." ); throw new Exception( "로그인이 필요한 서비스입니다." );
} }
Map<String, Object> map = new HashMap<String, Object>();
map.put("userid", userId);
Map<String, Object> result = loginService.selectWebMemberIn(map);
int cls = MyUtil.getIntegerFromObject( result.get("cls") );
if( cls == 2 ) {
// 발주기관 계정으로 조회한 경우, 본인의 영역에 해당하는 프로젝트만 조회한다.
String masterCompanyCode = MyUtil.getStringFromObject( result.get("master_company_code") );
HashMap<String, Object> spGetMasterCompanyDistrictParams = drillingInputService.getOrganizationUserGlGmGsGfCodes(userId);
String glDistrict = MyUtil.getStringFromObject( spGetMasterCompanyDistrictParams.get("v_gl") );
String gmDistrict = MyUtil.getStringFromObject( spGetMasterCompanyDistrictParams.get("v_gm") );
String gsDistrict = MyUtil.getStringFromObject( spGetMasterCompanyDistrictParams.get("v_gs") );
params.put("glDistrict", glDistrict);
params.put("gmDistrict", gmDistrict);
params.put("gsDistrict", gsDistrict);
}
try { try {
try { try {

View File

@ -444,7 +444,9 @@ public final class MyUtil {
return ((Long) obj).intValue(); return ((Long) obj).intValue();
} else if (obj instanceof Double) { } else if (obj instanceof Double) {
return ((Long)Math.round((Double)obj)).intValue(); return ((Long)Math.round((Double)obj)).intValue();
} } else if (obj instanceof BigDecimal) {
return ((BigDecimal)obj).intValue();
}
return null; return null;
} }

View File

@ -74,7 +74,8 @@
tgmd.GM_DISTRICT, tgmd.GM_DISTRICT,
tgsd.GS_DISTRICT, tgsd.GS_DISTRICT,
tcsi.CID, tcsi.CID,
tcsi.CONST_NAME tcsi.CONST_NAME,
tcsi.CONST_START_DATE
FROM FROM
TEMP_CONSTRUCT_SITE_INFO tcsi TEMP_CONSTRUCT_SITE_INFO tcsi
LEFT JOIN ( LEFT JOIN (
@ -112,7 +113,16 @@
tcsi.MASTER_COMPANY_TH_CODE = tgsd.GS_CODE tcsi.MASTER_COMPANY_TH_CODE = tgsd.GS_CODE
WHERE WHERE
tcsi.PROJECT_CODE IS NULL AND tcsi.PROJECT_CODE IS NULL AND
tcsi.CONST_NAME LIKE '%' || #{projectName} || '%' tcsi.CONST_NAME LIKE '%' || #{projectName} || '%'
<if test="glDistrict != null">
<![CDATA[ AND tgld.GL_CODE = #{glDistrict} ]]>
</if>
<if test="gmDistrict != null">
<![CDATA[ AND tgmd.GM_CODE = #{gmDistrict} ]]>
</if>
<if test="gsDistrict != null">
<![CDATA[ AND tgsd.GS_CODE = #{gsDistrict} ]]>
</if>
ORDER BY tcsi.CRT_DT DESC ORDER BY tcsi.CRT_DT DESC
</select> </select>

View File

@ -34,6 +34,97 @@ if (request.getSession().getAttribute("CLS") == null || "2".equals(request.getSe
<c:import url="/drilling/common/includeTopMenu.do" charEncoding="UTF-8" /> <c:import url="/drilling/common/includeTopMenu.do" charEncoding="UTF-8" />
<!-- header end--> <!-- header end-->
<style>
@keyframes shake {
0% { transform: translateX(0); }
10% { transform: translateX(-5px); }
20% { transform: translateX(5px); }
30% { transform: translateX(-5px); }
40% { transform: translateX(5px); }
50% { transform: translateX(-5px); }
60% { transform: translateX(5px); }
70% { transform: translateX(-5px); }
80% { transform: translateX(5px); }
90% { transform: translateX(-5px); }
100% { transform: translateX(0); }
}
.shake-animation {
animation: shake 0.6s;
}
/* The snackbar - position it at the bottom and in the middle of the screen */
#snackbar {
visibility: hidden; /* Hidden by default. Visible on click */
min-width: 250px; /* Set a default minimum width */
margin-left: -125px; /* Divide value of min-width by 2 */
background-color: #000000; /* Black background color */
color: #ff0000; /* White text color */
text-align: center; /* Centered text */
border-radius: 2px; /* Rounded borders */
padding: 16px; /* Padding */
position: fixed; /* Sit on top of the screen */
z-index: 1; /* Add a z-index if needed */
left: 50%; /* Center the snackbar */
bottom: 80px; /* 30px from the bottom */
font-weight: 500;
}
/* Show the snackbar when clicking on a button (class added with JavaScript) */
#snackbar.show {
visibility: visible; /* Show the snackbar */
/* Add animation: Take 0.5 seconds to fade in and out the snackbar.
However, delay the fade out process for 2.5 seconds */
-webkit-animation: fadein 0.5s, fadeout 0.5s 2.5s;
animation: fadein 0.5s, fadeout 0.5s 2.5s;
}
/* Animations to fade the snackbar in and out */
@-webkit-keyframes fadein {
from {bottom: 0; opacity: 0;}
to {bottom: 80px; opacity: 1;}
}
@keyframes fadein {
from {bottom: 0; opacity: 0;}
to {bottom: 80px; opacity: 1;}
}
@-webkit-keyframes fadeout {
from {bottom: 80px; opacity: 1;}
to {bottom: 0; opacity: 0;}
}
@keyframes fadeout {
from {bottom: 80px; opacity: 1;}
to {bottom: 0; opacity: 0;}
}
#suggestionList {
border: 1px solid #ccc;
width: 300px; /* 입력창 너비에 맞춰 조절 */
position_: absolute;
background-color: white;
display: none;
left: 91px;
top: 54px;
z-index: 3;
}
#suggestionList div {
padding: 5px;
cursor: pointer;
}
#suggestionList div:hover {
background-color: #f0f0f0;
}
#suggestionList div .organizational-structure {
color: red;
}
</style>
<!-- javascript start--> <!-- javascript start-->
<script type="text/javascript"> <script type="text/javascript">
@ -217,8 +308,98 @@ if (request.getSession().getAttribute("CLS") == null || "2".equals(request.getSe
} }
}); });
var projectNameInput = document.getElementById('const-name');
var suggestionListDiv = document.getElementById("suggestionList");
projectNameInput.onkeyup = function() {
var projectName = String(this.value).trim();
if (projectName.length > 0) {
$.ajax({
type : "GET",
data : {
projectName : projectName,
isProjectNameChecking : "true"
},
url : "/drilling-project-list.json",
dataType : "json",
success : function( json ) {
suggestionListDiv.innerHTML = ""; // 이전 목록 비우기
suggestionListDiv.style.display = "none";
var list = json.result.list;
var matchingProjects = [];
for (var i = 0; i < list.length; i++) {
matchingProjects.push(list[i]);
}
if (matchingProjects.length > 0) {
for (var i = 0; i < matchingProjects.length; i++) {
var suggestionItem = document.createElement("div");
var organHierarchy = " " + matchingProjects[i].glDistrict !== null ? matchingProjects[i].glDistrict : "";
if( matchingProjects[i].gmDistrict !== null ) {
organHierarchy = organHierarchy + " &gt; " + matchingProjects[i].gmDistrict;
}
if( matchingProjects[i].gsDistrict !== null ) {
organHierarchy = organHierarchy + " &gt; " + matchingProjects[i].gsDistrict;
}
suggestionItem.setAttribute('data-const-name', matchingProjects[i].constName);
suggestionItem.setAttribute('data-cid', matchingProjects[i].cid);
// 검색어를 굵게 표시
              var constName = matchingProjects[i].constName;
              var projectName = String(projectNameInput.value).trim();
              // 정규식으로 검색어를 찾고, 대소문자 구분 없이 처리
              var regex = new RegExp(projectName, "gi");
              var boldConstName = constName.replace(regex, '<b>' + projectName + '</b>');
suggestionItem.innerHTML = 
              '<span>' + boldConstName + '</span><br />\n' +
              '<span class="organizational-structure" data->' +
              "발주처: " + organHierarchy
              '</span>';
suggestionItem.onclick = function() {
projectNameInput.value = this.getAttribute('data-const-name');
suggestionListDiv.style.display = "none";
};
suggestionListDiv.appendChild(suggestionItem);
}
// suggestionListDiv 위치 설정
var rect = projectNameInput.getBoundingClientRect();
suggestionListDiv.style.position = 'absolute';
//suggestionListDiv.style.left = rect.left + 'px';
//suggestionListDiv.style.top = (rect.bottom + window.scrollY) + 'px';
//suggestionListDiv.style.float = 'left';
suggestionListDiv.style.width = rect.width + 'px';
suggestionListDiv.style.display = "block";
}
},
error: function(xhr, option, error){
alert(xhr.status); //오류코드
alert(error); //오류내용
}
});
}
};
// 사용자가 추천 목록 외부를 클릭하면 목록 숨기기 (선택적)
document.onclick = function(event) {
if (event.target !== projectNameInput && event.target !== suggestionListDiv && !suggestionListDiv.contains(event.target)) {
suggestionListDiv.style.display = "none";
}
};
}); });
</script> </script>
<!-- javascript end--> <!-- javascript end-->
@ -260,6 +441,7 @@ if (request.getSession().getAttribute("CLS") == null || "2".equals(request.getSe
<label class="input-label-display">검색</label> <label class="input-label-display">검색</label>
<input type="hidden" id="const-tag" name="const-tag" value="C" > <input type="hidden" id="const-tag" name="const-tag" value="C" >
<input type="search" id="const-name" name="const-name" class="input" placeholder="프로젝트명" title="" value=""> <input type="search" id="const-name" name="const-name" class="input" placeholder="프로젝트명" title="" value="">
<div id="suggestionList"></div>
<input type="date" id="const-start-date" name="const-start-date" > <input type="date" id="const-start-date" name="const-start-date" >
<span>~</span> <span>~</span>
<input type="date" id="const-end-date" name="const-end-date" > <input type="date" id="const-end-date" name="const-end-date" >

View File

@ -348,7 +348,6 @@ window.onload = function() {
resultData = json.result; resultData = json.result;
if(resultData == "false"){ if(resultData == "false"){
shakeAndHighlight(projectNameInput, json.message); shakeAndHighlight(projectNameInput, json.message);
//projectNameInput.value = '';
return false; return false;
} else { } else {
return true; return true;
@ -367,91 +366,76 @@ window.onload = function() {
}); });
var preProjectList = [ projectNameInput.onkeyup = function() {
"새로운 웹사이트 개발 프로젝트",
"새로운 나라만들기 프로젝트", var projectName = String(this.value).trim();
"모바일 앱 개편",
"데이터베이스 마이그레이션", if (projectName.length > 0) {
"클라우드 인프라 구축",
"인공지능 기반 서비스 개발", $.ajax({
"머신러닝 모델 학습", type : "GET",
"빅데이터 분석 플랫폼 구축", data : {
"사이버 보안 강화 프로젝트", projectName : projectName,
"소프트웨어 품질 개선", isProjectNameChecking : "true"
"사용자 인터페이스 디자인 개선" },
]; url : "/drilling-project-list.json",
dataType : "json",
projectNameInput.onkeyup = function() { success : function( json ) {
suggestionListDiv.innerHTML = ""; // 이전 목록 비우기
var projectName = this.value; suggestionListDiv.style.display = "none";
var list = json.result.list;
var matchingProjects = [];
for (var i = 0; i < list.length; i++) {
matchingProjects.push(list[i]);
}
if (matchingProjects.length > 0) {
for (var i = 0; i < matchingProjects.length; i++) {
var suggestionItem = document.createElement("div");
var organHierarchy = " " + matchingProjects[i].glDistrict !== null ? matchingProjects[i].glDistrict : "";
if( matchingProjects[i].gmDistrict !== null ) {
organHierarchy = organHierarchy + " &gt; " + matchingProjects[i].gmDistrict;
}
if( matchingProjects[i].gsDistrict !== null ) {
organHierarchy = organHierarchy + " &gt; " + matchingProjects[i].gsDistrict;
}
suggestionItem.setAttribute('data-const-name', matchingProjects[i].constName);
suggestionItem.setAttribute('data-cid', matchingProjects[i].cid);
suggestionItem.innerHTML =
'<span>' + matchingProjects[i].constName + '</span><br />\n' +
'<span class="organizational-structure" data->' +
"발주처: " + organHierarchy
'</span>';
suggestionItem.onclick = function() {
projectNameInput.value = this.getAttribute('data-const-name');
document.getElementById("TEMP_CONSTRUCT_SITE_INFO-CID").value = this.getAttribute('data-cid');
suggestionListDiv.style.display = "none";
};
suggestionListDiv.appendChild(suggestionItem);
}
// suggestionListDiv 위치 설정
var rect = projectNameInput.getBoundingClientRect();
suggestionListDiv.style.position = 'absolute';
//suggestionListDiv.style.left = rect.left + 'px';
//suggestionListDiv.style.top = (rect.bottom + window.scrollY) + 'px';
//suggestionListDiv.style.float = 'left';
suggestionListDiv.style.width = rect.width + 'px';
suggestionListDiv.style.display = "block";
}
},
error: function(xhr, option, error){
alert(xhr.status); //오류코드
alert(error); //오류내용
}
});
}
if (projectName.length > 0) { };
$.ajax({
type : "GET",
data : {
projectName : projectName,
isProjectNameChecking : "true"
},
url : "/drilling-project-list.json",
dataType : "json",
success : function( json ) {
suggestionListDiv.innerHTML = ""; // 이전 목록 비우기
suggestionListDiv.style.display = "none";
var list = json.result.list;
var matchingProjects = [];
for (var i = 0; i < list.length; i++) {
matchingProjects.push(list[i]);
}
if (matchingProjects.length > 0) {
for (var i = 0; i < matchingProjects.length; i++) {
var suggestionItem = document.createElement("div");
var organHierarchy = " " + matchingProjects[i].glDistrict !== null ? matchingProjects[i].glDistrict : "";
if( matchingProjects[i].gmDistrict !== null ) {
organHierarchy = organHierarchy + " &gt; " + matchingProjects[i].gmDistrict;
}
if( matchingProjects[i].gsDistrict !== null ) {
organHierarchy = organHierarchy + " &gt; " + matchingProjects[i].gsDistrict;
}
suggestionItem.setAttribute('data-const-name', matchingProjects[i].constName);
suggestionItem.setAttribute('data-cid', matchingProjects[i].cid);
suggestionItem.innerHTML =
'<span>' + matchingProjects[i].constName + '</span><br />\n' +
'<span class="organizational-structure" data->' +
"발주처: " + organHierarchy
'</span>';
suggestionItem.onclick = function() {
projectNameInput.value = this.getAttribute('data-const-name');
document.getElementById("TEMP_CONSTRUCT_SITE_INFO-CID").value = this.getAttribute('data-cid');
suggestionListDiv.style.display = "none";
};
suggestionListDiv.appendChild(suggestionItem);
}
// suggestionListDiv 위치 설정
var rect = projectNameInput.getBoundingClientRect();
suggestionListDiv.style.position = 'absolute';
//suggestionListDiv.style.left = rect.left + 'px';
//suggestionListDiv.style.top = (rect.bottom + window.scrollY) + 'px';
//suggestionListDiv.style.float = 'left';
suggestionListDiv.style.width = rect.width + 'px';
suggestionListDiv.style.display = "block";
}
},
error: function(xhr, option, error){
alert(xhr.status); //오류코드
alert(error); //오류내용
}
});
}
};