조사원 > 조사원 팝업: 조사원 선택 및 상세 조회 기능 구현, 그리드 UI 추가, 팝업 기능 연동

dev
박성영 4 months ago
parent f6f1f1a2e6
commit 71c536af35

@ -0,0 +1,89 @@
package go.kr.project.crdn.crndRegistAndView.exmnrPop.controller;
import egovframework.constant.TilesConstants;
import egovframework.util.ApiResponseUtil;
import go.kr.project.crdn.crndRegistAndView.exmnrPop.model.ExmnrPopVO;
import go.kr.project.crdn.crndRegistAndView.exmnrPop.model.ExmnrPopSearchVO;
import go.kr.project.crdn.crndRegistAndView.exmnrPop.service.ExmnrPopService;
import go.kr.project.system.user.model.SystemUserVO;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
*
*/
@Tag(name = "조사원 팝업", description = "조사원 팝업 API")
@Slf4j
@Controller
@RequiredArgsConstructor
@RequestMapping("/crdn/crndRegistAndView/exmnrPop")
public class ExmnrPopController {
private final ExmnrPopService exmnrPopService;
/**
*
* @param model
* @return
*/
@Operation(summary = "조사원 선택 팝업 페이지", description = "조사원을 선택할 수 있는 팝업 페이지를 표시합니다.")
@GetMapping("/popup.do")
public String exmnrPopup(Model model) {
log.debug("조사원 선택 팝업 페이지 호출");
return "crdn/crndRegistAndView/exmnrPop/popup" + TilesConstants.POPUP;
}
/**
* (API)
* @param searchVO
* @return
*/
@Operation(summary = "조사원 목록 조회", description = "검색 조건에 따른 조사원 목록을 조회합니다.")
@PostMapping("/list.ajax")
public ResponseEntity<?> selectExmnrPopList(
@Parameter(description = "검색 조건") @ModelAttribute ExmnrPopSearchVO searchVO) {
log.debug("조사원 팝업 목록 조회 요청 - 검색 조건: {}", searchVO);
// 1. 총 개수 조회
int totalCount = exmnrPopService.selectExmnrPopListTotalCount(searchVO);
// 2. 응답 데이터 구성
searchVO.setTotalCount(totalCount);
// 3. 페이징 처리 (client-side paging이므로 전체 데이터 조회)
searchVO.setPagingYn("N");
// 조사원 목록 조회
List<ExmnrPopVO> exmnrList = exmnrPopService.selectExmnrPopList(searchVO);
return ApiResponseUtil.successWithGrid(exmnrList, searchVO);
}
/**
* (API)
* @param exmnrId ID
* @return
*/
@Operation(summary = "조사원 상세 조회", description = "조사원 ID로 조사원 상세 정보를 조회합니다.")
@GetMapping("/detail/{exmnrId}.do")
public String selectExmnrPopDetail(
Model model,
@Parameter(description = "조사원 ID") @PathVariable String exmnrId) {
model.addAttribute("exmnrVO", exmnrPopService.selectExmnrPopDetail(exmnrId));
return "crdn/crndRegistAndView/exmnrPop/form"+ TilesConstants.BASE;
}
}

@ -0,0 +1,36 @@
package go.kr.project.crdn.crndRegistAndView.exmnrPop.mapper;
import go.kr.project.crdn.crndRegistAndView.exmnrPop.model.ExmnrPopVO;
import go.kr.project.crdn.crndRegistAndView.exmnrPop.model.ExmnrPopSearchVO;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
/**
*
*/
@Mapper
public interface ExmnrPopMapper {
/**
* ()
* @param searchVO
* @return
*/
List<ExmnrPopVO> selectExmnrPopList(ExmnrPopSearchVO searchVO);
/**
*
* @param searchVO
* @return
*/
int selectExmnrPopListTotalCount(ExmnrPopSearchVO searchVO);
/**
*
* @param exmnrId ID
* @return
*/
ExmnrPopVO selectExmnrPopDetail(String exmnrId);
}

@ -0,0 +1,16 @@
package go.kr.project.crdn.crndRegistAndView.exmnrPop.model;
import go.kr.project.common.model.PagingVO;
import lombok.*;
@EqualsAndHashCode(callSuper=true)
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class ExmnrPopSearchVO extends PagingVO {
private String schExmnr;
private String schSggCd;
private String delYn = "N";
}

@ -0,0 +1,29 @@
package go.kr.project.crdn.crndRegistAndView.exmnrPop.model;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.*;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class ExmnrPopVO {
private String exmnrId;
private String sggCd;
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Seoul")
private LocalDateTime regDt;
private String rgtr;
private String delYn;
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Seoul")
private LocalDateTime delDt;
private String dltr;
private String exmnr;
}

@ -0,0 +1,34 @@
package go.kr.project.crdn.crndRegistAndView.exmnrPop.service;
import go.kr.project.crdn.crndRegistAndView.exmnrPop.model.ExmnrPopVO;
import go.kr.project.crdn.crndRegistAndView.exmnrPop.model.ExmnrPopSearchVO;
import java.util.List;
/**
*
*/
public interface ExmnrPopService {
/**
* ()
* @param searchVO
* @return
*/
List<ExmnrPopVO> selectExmnrPopList(ExmnrPopSearchVO searchVO);
/**
*
* @param searchVO
* @return
*/
int selectExmnrPopListTotalCount(ExmnrPopSearchVO searchVO);
/**
*
* @param exmnrId ID
* @return
*/
ExmnrPopVO selectExmnrPopDetail(String exmnrId);
}

@ -0,0 +1,56 @@
package go.kr.project.crdn.crndRegistAndView.exmnrPop.service.impl;
import go.kr.project.crdn.crndRegistAndView.exmnrPop.mapper.ExmnrPopMapper;
import go.kr.project.crdn.crndRegistAndView.exmnrPop.model.ExmnrPopVO;
import go.kr.project.crdn.crndRegistAndView.exmnrPop.model.ExmnrPopSearchVO;
import go.kr.project.crdn.crndRegistAndView.exmnrPop.service.ExmnrPopService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.List;
/**
*
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class ExmnrPopServiceImpl implements ExmnrPopService {
private final ExmnrPopMapper exmnrPopMapper;
/**
* ()
* @param searchVO
* @return
*/
@Override
public List<ExmnrPopVO> selectExmnrPopList(ExmnrPopSearchVO searchVO) {
log.debug("조사원 팝업 목록 조회 - 검색 조건: {}", searchVO);
return exmnrPopMapper.selectExmnrPopList(searchVO);
}
/**
*
* @param searchVO
* @return
*/
@Override
public int selectExmnrPopListTotalCount(ExmnrPopSearchVO searchVO) {
log.debug("조사원 팝업 목록 총 건수 조회 - 검색 조건: {}", searchVO);
return exmnrPopMapper.selectExmnrPopListTotalCount(searchVO);
}
/**
*
* @param exmnrId ID
* @return
*/
@Override
public ExmnrPopVO selectExmnrPopDetail(String exmnrId) {
log.debug("조사원 팝업 상세 조회 - 조사원 ID: {}", exmnrId);
return exmnrPopMapper.selectExmnrPopDetail(exmnrId);
}
}

@ -0,0 +1,68 @@
<?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">
<mapper namespace="go.kr.project.crdn.crndRegistAndView.exmnrPop.mapper.ExmnrPopMapper">
<!-- 조사원 팝업 목록 조회 -->
<select id="selectExmnrPopList" parameterType="go.kr.project.crdn.crndRegistAndView.exmnrPop.model.ExmnrPopSearchVO"
resultType="go.kr.project.crdn.crndRegistAndView.exmnrPop.model.ExmnrPopVO">
SELECT
e.EXMNR_ID as exmnrId,
e.SGG_CD as sggCd,
e.REG_DT as regDt,
e.RGTR as rgtr,
e.DEL_YN as delYn,
e.DEL_DT as delDt,
e.DLTR as dltr,
e.EXMNR as exmnr
FROM tb_exmnr e
WHERE 1=1
<if test='delYn != null and delYn != ""'>
AND e.DEL_YN = #{delYn}
</if>
<if test='schExmnr != null and schExmnr != ""'>
AND e.EXMNR LIKE CONCAT('%', #{schExmnr}, '%')
</if>
<if test='schSggCd != null and schSggCd != ""'>
AND e.SGG_CD = #{schSggCd}
</if>
ORDER BY e.REG_DT DESC, e.EXMNR_ID DESC
<if test="pagingYn != null and pagingYn == 'Y'.toString()">
LIMIT #{startNum}, #{endNum}
</if>
</select>
<!-- 조사원 팝업 목록 총 건수 조회 -->
<select id="selectExmnrPopListTotalCount" parameterType="go.kr.project.crdn.crndRegistAndView.exmnrPop.model.ExmnrPopSearchVO"
resultType="int">
SELECT COUNT(*)
FROM tb_exmnr e
WHERE 1=1
<if test='delYn != null and delYn != ""'>
AND e.DEL_YN = #{delYn}
</if>
<if test='schExmnr != null and schExmnr != ""'>
AND e.EXMNR LIKE CONCAT('%', #{schExmnr}, '%')
</if>
<if test='schSggCd != null and schSggCd != ""'>
AND e.SGG_CD = #{schSggCd}
</if>
</select>
<!-- 조사원 팝업 상세 조회 -->
<select id="selectExmnrPopDetail" parameterType="String"
resultType="go.kr.project.crdn.crndRegistAndView.exmnrPop.model.ExmnrPopVO">
SELECT
e.EXMNR_ID as exmnrId,
e.SGG_CD as sggCd,
e.REG_DT as regDt,
e.RGTR as rgtr,
e.DEL_YN as delYn,
e.DEL_DT as delDt,
e.DLTR as dltr,
e.EXMNR as exmnr
FROM tb_exmnr e
WHERE e.EXMNR_ID = #{exmnrId}
</select>
</mapper>

@ -0,0 +1,311 @@
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<div class="popup_wrap">
<div class="popup_inner">
<div class="popup_tit">
<h2 class="tit">조사원 선택</h2>
<a href="#" class="pop-x-btn modalclose"></a>
</div>
<div class="popup_con">
<!-- 검색 영역 -->
<div class="gs_b_top">
<ul class="lef">
<li class="th">검색어</li>
<li>
<input type="text" id="schExmnr" name="schExmnr" class="input" placeholder="조사원명을 입력하세요" style="width: 200px;">
</li>
</ul>
<ul class="rig">
<li>
<button type="button" id="search_btn" class="newbtnss bg1 smallb">검색</button>
</li>
</ul>
</div>
<div class="gs_booking">
<div class="row">
<div class="col-sm-12">
<div class="box_column">
<ul class="box_title" style="display: flex; justify-content: space-between; align-items: center;">
<li class="tit">단속 목록</li>
<li class="rig">
<span id="totalCount" class="total-count" style="padding-left: 25px;padding-right: 25px;">총 0건</span>
<select id="perPageSelect" class="input" style="width: 112px; ">
<option value="15">페이지당 15</option>
<option value="50">페이지당 50</option>
<option value="100">페이지당 100</option>
</select>
<span class="page_number"><span id="currentPage"></span><span class="bar">/</span><span id="totalPages"></span> Pages</span>
</li>
</ul>
<div class="containers">
<div id="grid"></div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="popup_foot">
<a href="#" id="selectBtn" class="newbtns bg4" onclick="ExmnrPopPopup.selectExmnr();">선택</a>
<a href="#" class="newbtns bg1 modalclose">닫기</a>
</div>
</div>
</div>
<script type="text/javascript">
/**
* 조사원 팝업 선택 모듈
*/
var ExmnrPopPopup = {
grid: null,
selectedRows: [],
/**
* 초기화
*/
init: function() {
this.initGrid();
this.searchExmnrList();
this.bindEvents();
},
/**
* 이벤트 바인딩
*/
bindEvents: function() {
var self = this;
// 엔터키 검색
$('#schExmnr').on('keypress', function(e) {
if (e.which === 13) {
self.searchExmnrList();
}
});
},
/**
* 그리드 초기화
*/
initGrid: function() {
var self = this;
// 데이터소스 설정
var dataSource = {
api: {
readData: {
url: '<c:url value="/crdn/crndRegistAndView/exmnrPop/list" />',
method: 'POST',
initParams: self.getSearchParams()
}
},
contentType: 'application/x-www-form-urlencoded',
serializer: function(params) {
return $.param(params.data);
}
};
// 그리드 설정
var gridConfig = new XitTuiGridConfig();
gridConfig.setOptDataSource(dataSource);
gridConfig.setOptGridId('exmnrGrid');
gridConfig.setOptGridHeight(400);
gridConfig.setOptRowHeight(30);
gridConfig.setOptRowHeaderType('checkbox'); // 체크박스 선택
gridConfig.setOptUseClientSort(true);
// 클라이언트 사이드 페이징 설정
gridConfig.setOptPageOptions({
useClient: true, // 클라이언트 페이징 사용
perPage: 10 // 페이지당 표시 건수
});
gridConfig.setOptColumns(this.getGridColumns());
// 그리드 생성
this.grid = new XitTuiGrid(gridConfig);
// 그리드 이벤트 바인딩
this.grid.on('check', function(ev) {
self.onRowCheck(ev);
});
this.grid.on('uncheck', function(ev) {
self.onRowUncheck(ev);
});
this.grid.on('checkAll', function(ev) {
self.onCheckAll(ev);
});
this.grid.on('uncheckAll', function(ev) {
self.onUncheckAll(ev);
});
},
/**
* 그리드 컬럼 정의
*/
getGridColumns: function() {
return [
{
header: '번호',
name: '_rowNum',
align: 'center',
width: 60,
sortable: false,
formatter: function(e) {
return e.row.rowKey + 1;
}
},
{
header: '조사원 ID',
name: 'exmnrId',
align: 'center',
width: 120,
sortable: true
},
{
header: '조사원명',
name: 'exmnr',
align: 'left',
width: 200,
sortable: true
},
{
header: '시군구 코드',
name: 'sggCd',
align: 'center',
width: 100,
sortable: true
},
{
header: '등록일시',
name: 'regDt',
align: 'center',
width: 150,
sortable: true,
formatter: function(e) {
if (e.value) {
return e.value.substring(0, 19); // YYYY-MM-DD HH:MM:SS 형식
}
return '';
}
}
];
},
/**
* 검색 조건 가져오기
*/
getSearchParams: function() {
return {
schExmnr: $('#schExmnr').val() || '',
delYn: 'N' // 삭제되지 않은 조사원만 조회
};
},
/**
* 조사원 목록 검색
*/
searchExmnrList: function() {
var self = this;
var params = this.getSearchParams();
// 그리드 데이터 새로고침
this.grid.reloadData(params).then(function() {
// 총 건수 업데이트
var totalCount = self.grid.getRowCount();
$('#totalCount').text('총 ' + totalCount + '건');
// 선택 초기화
self.selectedRows = [];
}).catch(function(error) {
console.error('조사원 목록 조회 실패:', error);
alert('조사원 목록을 조회하는 중 오류가 발생했습니다.');
});
},
/**
* 검색 조건 초기화
*/
resetSearch: function() {
$('#schExmnr').val('');
this.searchExmnrList();
},
/**
* 행 체크 이벤트
*/
onRowCheck: function(ev) {
var rowData = this.grid.getRow(ev.rowKey);
// 중복 방지
var existIndex = this.selectedRows.findIndex(function(row) {
return row.exmnrId === rowData.exmnrId;
});
if (existIndex === -1) {
this.selectedRows.push(rowData);
}
},
/**
* 행 체크 해제 이벤트
*/
onRowUncheck: function(ev) {
var rowData = this.grid.getRow(ev.rowKey);
this.selectedRows = this.selectedRows.filter(function(row) {
return row.exmnrId !== rowData.exmnrId;
});
},
/**
* 전체 체크 이벤트
*/
onCheckAll: function(ev) {
var self = this;
this.selectedRows = [];
// 현재 페이지의 모든 행 데이터 추가
this.grid.getData().forEach(function(row) {
self.selectedRows.push(row);
});
},
/**
* 전체 체크 해제 이벤트
*/
onUncheckAll: function(ev) {
this.selectedRows = [];
},
/**
* 조사원 선택
*/
selectExmnr: function() {
if (this.selectedRows.length === 0) {
alert('선택된 조사원이 없습니다.');
return;
}
// 부모 창으로 선택된 조사원 데이터 전달
if (window.opener && typeof window.opener.onExmnrSelected === 'function') {
window.opener.onExmnrSelected(this.selectedRows);
}
// 팝업 닫기
window.close();
}
};
// 페이지 로드 완료 시 초기화
$(document).ready(function() {
ExmnrPopPopup.init();
});
</script>

@ -74,7 +74,8 @@
<th class="th">조사원</th>
<td colspan="3">
<input type="text" id="exmnr" name="exmnr" class="input"
value="${data.exmnr}" maxlength="100" />
value="${data.exmnr}" maxlength="100" style="width: 80%;" />
<button id="btnExmnrSelect" class="newbtn bg1" type="button">조사원 선택</button>
</td>
</tr>
<tr>
@ -168,12 +169,30 @@
language: "kr"
});
// 조사원 선택 버튼
$("#btnExmnrSelect").on('click', function() {
self.openExmnrPopup();
});
},
cancel: function() {
window.close();
},
/**
* 조사원 선택 팝업 열기
*/
openExmnrPopup: function() {
var popupUrl = '<c:url value="/crdn/crndRegistAndView/exmnrPop/popup.do" />';
var popupName = 'exmnrPopup';
var popupOptions = 'width=900,height=700,scrollbars=yes,resizable=yes,left=' +
Math.round((screen.width - 900) / 2) + ',top=' +
Math.round((screen.height - 700) / 2);
window.open(popupUrl, popupName, popupOptions);
},
save: function() {
if (!this.validate()) return;
@ -276,5 +295,22 @@
CrdnPopup.init();
});
/**
* 조사원 선택 팝업에서 호출되는 콜백 함수
* @param selectedExmnrs 선택된 조사원 배열
*/
window.onExmnrSelected = function(selectedExmnrs) {
if (selectedExmnrs && selectedExmnrs.length > 0) {
// 첫 번째 선택된 조사원의 이름을 입력 필드에 설정
var exmnr = selectedExmnrs[0];
$('#exmnr').val(exmnr.exmnr);
console.log('선택된 조사원:', exmnr);
// 추가적인 처리가 필요하다면 여기에 구현
// 예: 조사원 ID를 숨겨진 필드에 저장 등
}
};
})(jQuery);
</script>

@ -59,7 +59,7 @@
<link rel="stylesheet" type="text/css" href="<c:url value='/plugins/tuiGrid/tui-pagination.css' />">
<link rel="stylesheet" type="text/css" href="<c:url value='/plugins/tuiGrid/tui-time-picker.css' />">
<link rel="stylesheet" type="text/css" href="<c:url value='/plugins/tuiGrid/tui-grid.css' />">
<script type="text/javascript" src="<c:url value='/plugins/tuiGrid/tui-code-snippet.js' />"></script>
<script type="text/javascript" src="<c:url value='/plugins/tuiGrid/xlsx.full.min.js' />"></script>
<script type="text/javascript" src="<c:url value='/plugins/tuiGrid/tui-pagination.js' />"></script>
<script type="text/javascript" src="<c:url value='/plugins/tuiGrid/tui-date-picker.js' />"></script>
<script type="text/javascript" src="<c:url value='/plugins/tuiGrid/tui-time-picker.js' />"></script>

Loading…
Cancel
Save