From 3f743ac3d605382d0ea1b08e7e282ab6f7e66dfa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=84=B1=EC=98=81?= Date: Fri, 26 Sep 2025 14:04:29 +0900 Subject: [PATCH] =?UTF-8?q?=EB=B6=88=EB=B2=95=ED=96=89=EC=9C=84=20?= =?UTF-8?q?=EC=A0=95=EB=B3=B4=20-=20=EC=A1=B0=EC=B9=98=EC=97=90=20?= =?UTF-8?q?=EB=8C=80=ED=95=9C=20=EB=A1=9C=EC=A7=81=20=EC=9E=AC=EA=B5=AC?= =?UTF-8?q?=EC=84=B1=20=EC=A7=84=ED=96=89=20=EC=A4=91...?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/CrdnActInfoController.java | 56 +-- .../crdnActInfo/crdnActnInfoRegistPopup.jsp | 371 ++++++++++-------- 2 files changed, 204 insertions(+), 223 deletions(-) diff --git a/src/main/java/go/kr/project/crdn/crndRegistAndView/crdnActInfo/controller/CrdnActInfoController.java b/src/main/java/go/kr/project/crdn/crndRegistAndView/crdnActInfo/controller/CrdnActInfoController.java index 7fd45da..3902371 100644 --- a/src/main/java/go/kr/project/crdn/crndRegistAndView/crdnActInfo/controller/CrdnActInfoController.java +++ b/src/main/java/go/kr/project/crdn/crndRegistAndView/crdnActInfo/controller/CrdnActInfoController.java @@ -327,6 +327,7 @@ public class CrdnActInfoController { @GetMapping("/photoView.do") public String photoView( @Parameter(description = "행위정보ID") @RequestParam String actInfoId, + @Parameter(description = "조치정보ID") @RequestParam(required = false) String actnInfoId, @Parameter(description = "사진순번") @RequestParam String crdnPhotoSn, @Parameter(description = "단속조치사진구분") @RequestParam String crdnPhotoSeCd, Model model) { @@ -336,6 +337,7 @@ public class CrdnActInfoController { // 중요한 로직 주석: 해당 행위정보의 모든 사진 목록 조회 CrdnPhotoVO searchVO = CrdnPhotoVO.builder() .actInfoId(actInfoId) + .actnInfoId(actnInfoId) .crdnPhotoSeCd(crdnPhotoSeCd) // 단속 사진 .build(); @@ -610,9 +612,7 @@ public class CrdnActInfoController { List photoList = photoService.selectPhotoList(photoVO); - return ApiResponseUtil.success( - new HashMap() {{ put("photoList", photoList); }} - ); + return ApiResponseUtil.success(photoList); } catch (Exception e) { log.error("조치사진 목록 조회 중 오류 발생: {}", actnInfoId, e); @@ -662,54 +662,4 @@ public class CrdnActInfoController { } } - /** - * 조치사진 원본 보기 페이지 (View) - * 중요한 로직 주석: 조치사진을 원본 크기로 보여주는 팝업 페이지를 반환한다. - * @param actnInfoId 조치정보 ID - * @param crdnPhotoSn 사진순번 - * @param model 뷰에 전달할 데이터 모델 - * @return 조치사진 보기 JSP 페이지 경로 - */ - @Operation(summary = "조치사진 원본 보기", description = "조치사진을 원본 크기로 보여주는 페이지를 반환합니다.") - @GetMapping("/actnPhotoView.do") - public String actnPhotoView( - @Parameter(description = "조치정보ID") @RequestParam String actnInfoId, - @Parameter(description = "사진순번") @RequestParam String crdnPhotoSn, - Model model) { - - log.debug("조치사진 원본 보기 요청 - actnInfoId: {}, crdnPhotoSn: {}", actnInfoId, crdnPhotoSn); - - try { - // 중요한 로직 주석: 조치사진 목록 조회 (같은 조치정보의 다른 사진들도 함께) - CrdnPhotoVO searchVO = CrdnPhotoVO.builder() - .actnInfoId(actnInfoId) - .crdnPhotoSeCd("2") // 조치사진 - .build(); - - List photoList = photoService.selectPhotoList(searchVO); - - // 중요한 로직 주석: 현재 보고 있는 사진의 인덱스 찾기 - int currentIndex = 0; - for (int i = 0; i < photoList.size(); i++) { - if (photoList.get(i).getCrdnPhotoSn().equals(crdnPhotoSn)) { - currentIndex = i; - break; - } - } - - // 중요한 로직 주석: JSP에서 사용할 데이터 전달 - model.addAttribute("actnInfoId", actnInfoId); - model.addAttribute("crdnPhotoSn", crdnPhotoSn); - model.addAttribute("photoList", photoList); - model.addAttribute("currentIndex", currentIndex); - model.addAttribute("totalCount", photoList.size()); - - return "crdn/crndRegistAndView/crdnActInfo/actnPhotoView"; - - } catch (Exception e) { - log.error("조치사진 원본 보기 중 오류 발생: actnInfoId={}, crdnPhotoSn={}", actnInfoId, crdnPhotoSn, e); - model.addAttribute("errorMessage", "조치사진을 불러올 수 없습니다."); - return "error/500"; - } - } } \ No newline at end of file diff --git a/src/main/webapp/WEB-INF/views/crdn/crndRegistAndView/crdnActInfo/crdnActnInfoRegistPopup.jsp b/src/main/webapp/WEB-INF/views/crdn/crndRegistAndView/crdnActInfo/crdnActnInfoRegistPopup.jsp index 56ccb4b..9c50647 100644 --- a/src/main/webapp/WEB-INF/views/crdn/crndRegistAndView/crdnActInfo/crdnActnInfoRegistPopup.jsp +++ b/src/main/webapp/WEB-INF/views/crdn/crndRegistAndView/crdnActInfo/crdnActnInfoRegistPopup.jsp @@ -124,15 +124,188 @@ * 조치정보 관리 팝업 JavaScript * 중요한 로직 주석: crdnActInfoRegistPopup.jsp와 동일한 구조, levyPrvntcPopup.jsp의 탭 패턴 적용 */ - var CrdnActnInfoManage = { + var crdnActnInfoRegistPopup = { // 기본 정보 actInfoId: '${actInfoId}', crdnYr: '${crdnYr}', crdnNo: '${crdnNo}', pstnInfoId: '${pstnInfoId}', + actnInfoIdSelect: null, - // 그리드 인스턴스 - grid: null, + /** + * 그리드 관련 객체 + */ + grid: { + /** + * 그리드 인스턴스 + */ + instance: null, + + /** + * 그리드 설정 초기화 + * @returns {Object} 그리드 설정 객체 + */ + initConfig: function() { + // 데이터 소스 설정 + var dataSource = this.createDataSource(); + + // 그리드 설정 객체 생성 + var gridConfig = new XitTuiGridConfig(); + + // 기본 설정 + gridConfig.setOptDataSource(dataSource); + gridConfig.setOptGridId('actnInfoGrid'); + gridConfig.setOptGridHeight(150); + gridConfig.setOptRowHeight(30); + gridConfig.setOptUseClientSort(true); + gridConfig.setOptRowHeaderType(''); + gridConfig.setOptColumns(this.getGridColumns()); + + return gridConfig; + }, + + /** + * 그리드 컬럼 정의 + * @returns {Array} 그리드 컬럼 배열 + */ + getGridColumns: function() { + return [ + { + header: '조치정보ID', + name: 'actnInfoId', + hidden: true + }, + { + header: '순번', + name: 'rownum', + width: 60, + align: 'center' + }, + { + header: '조치일자', + name: 'actnYmd', + width: 100, + align: 'center', + formatter: function(e) { + return e.value ? moment(e.value).format('YYYY-MM-DD') : ''; + } + }, + { + header: '조치면적(㎡)', + name: 'actnArea', + width: 100, + align: 'right', + formatter: function(e) { + return e.value ? parseFloat(e.value).toLocaleString() : '-'; + } + }, + { + header: '조치비고', + name: 'actnRmrk', + minWidth: 200, + }, + { + header: '수정일시', + name: 'mdfcnDt', + width: 130, + align: 'center', + } + ]; + }, + + /** + * 데이터 소스 생성 + * @returns {Object} 데이터 소스 설정 객체 + */ + createDataSource: function() { + var self = crdnActnInfoRegistPopup; + return { + api: { + readData: { + url: '', + method: 'POST', + contentType: 'application/x-www-form-urlencoded', + processData: true + } + }, + initialRequest: false, + serializer: function(params) { + var defaultParams = $.param(params); + var extra = $.param({ actInfoId: self.actInfoId }); + return defaultParams + '&' + extra; + } + }; + }, + + /** + * 그리드 인스턴스 생성 + */ + create: function() { + var gridConfig = this.initConfig(); + var Grid = tui.Grid; + this.instance = gridConfig.instance(Grid); + + + // 그리드 테마 설정 + Grid.applyTheme('striped'); + + this.gridBindEvents(); + }, + + /** + * 그리드 이벤트 바인딩 + */ + gridBindEvents: function() { + var self = this; + + this.instance.on('successResponse', function(ev) { + var responseObj = JSON.parse(ev.xhr.response); + var totalCount = 0; + if (responseObj && responseObj.data && responseObj.data.contents) { + totalCount = responseObj.data.contents.length; + $('#actnTotalCount').text('총 ' + totalCount + '건'); + } + }); + + this.instance.on('onGridUpdated', function() { + const firstVisibleColumn = self.instance.getColumns().find(column => !column.hidden); + var allRows = self.instance.getData(); + var rowKey = null; + if (allRows && allRows.length > 0) { + allRows.forEach(function(row) { + if( crdnActnInfoRegistPopup.actnInfoIdSelect === null ){ + rowKey = allRows[0].rowKey; + crdnActnInfoRegistPopup.actnInfoIdSelect = row.actnInfoId; + }else if (crdnActnInfoRegistPopup.actnInfoIdSelect === row.actnInfoId) { + rowKey = row.rowKey + } + // 2. 행 전체 선택 (파란색 배경) + self.instance.setSelectionRange({ + start: [rowKey, 0], + end: [rowKey, self.instance.getColumns().length - 1] + }); + // 2. 첫 번째 보이는 컬럼으로 포커스 이동 및 스크롤 + self.instance.focus(rowKey, firstVisibleColumn.name, true); + }); + } + }); + + this.instance.on('focusChange', function(ev) { + if (ev.rowKey !== null) { + crdnActnInfoRegistPopup.actnInfoIdSelect = ev.actnInfoId; + var rowData = self.grid.instance.getRow(ev.rowKey); + self.loadActnInfoToForm(rowData); + } + }); + + this.instance.on('click', function(ev) { + if (ev.rowKey !== null) { + crdnActnInfoRegistPopup.actnInfoIdSelect = ev.actnInfoId; + } + }); + + } + }, // 선택된 조치사진 파일들 actnSelectedFiles: [], @@ -141,7 +314,7 @@ * 초기화 */ init: function() { - this.initGrid(); + this.grid.create(); this.eventBindEvents(); this.loadActnInfoList(); }, @@ -164,11 +337,6 @@ self.switchTab($(this).parent().data('tab')); }); - // 조치정보 추가 버튼 - $('#btnAddActn').on('click', function() { - self.clearActnForm(); - }); - // 조치정보 저장 버튼 $('#btnSaveActn').on('click', function() { self.saveActnInfo(); @@ -218,138 +386,12 @@ } }, - - /** - * 조치정보 그리드 초기화 - */ - initGrid: function() { - var self = this; - - // 데이터 소스 설정 - var dataSource = { - api: { - readData: { - url: '', - method: 'POST', - contentType: 'application/x-www-form-urlencoded', - processData: true - } - }, - initialRequest: false, - serializer: function(params) { - var defaultParams = $.param(params); - var extra = $.param({ actInfoId: self.actInfoId }); - return defaultParams + '&' + extra; - } - }; - - // 그리드 설정 객체 생성 - var gridConfig = new XitTuiGridConfig(); - - // 기본 설정 - gridConfig.setOptDataSource(dataSource); - gridConfig.setOptGridId('actnInfoGrid'); - gridConfig.setOptGridHeight(150); - gridConfig.setOptRowHeight(30); - gridConfig.setOptUseClientSort(true); - gridConfig.setOptRowHeaderType(''); - - gridConfig.setOptColumns([ - { - header: '조치정보ID', - name: 'actnInfoId', - hidden: true - }, - { - header: '순번', - name: 'rownum', - width: 60, - align: 'center' - }, - { - header: '조치일자', - name: 'actnYmd', - width: 100, - align: 'center', - formatter: function(e) { - return e.value ? moment(e.value).format('YYYY-MM-DD') : ''; - } - }, - { - header: '조치면적(㎡)', - name: 'actnArea', - width: 100, - align: 'right', - formatter: function(e) { - return e.value ? parseFloat(e.value).toLocaleString() : '-'; - } - }, - { - header: '조치비고', - name: 'actnRmrk', - minWidth: 200, - }, - { - header: '수정일시', - name: 'mdfcnDt', - width: 130, - align: 'center', - } - ]); - - // 그리드 인스턴스 생성 - var Grid = tui.Grid; - this.grid = gridConfig.instance(Grid); - - // 그리드 테마 설정 - Grid.applyTheme('striped'); - - // 그리드 이벤트 바인딩 - this.bindGridEvents(); - }, - - /** - * 그리드 이벤트 바인딩 - */ - bindGridEvents: function() { - var self = this; - - // 데이터 로딩 완료 이벤트 - this.grid.on('successResponse', function(ev) { - var responseObj = JSON.parse(ev.xhr.response); - var totalCount = 0; - if (responseObj && responseObj.data && responseObj.data.contents) { - totalCount = responseObj.data.contents.length; - $('#actnTotalCount').text('총 ' + totalCount + '건'); - } - }); - - // 그리드 렌더링 및 데이터 업데이트 완료 이벤트 - this.grid.on('onGridUpdated', function() { - var allRows = self.grid.getData(); - if (allRows && allRows.length > 0) { - // 첫 번째 행 선택 - //self.grid.focus(0, 'actnYmd', true); - //self.loadActnInfoToForm(allRows[0]); - } - }); - - // 행 포커스 변경 이벤트 - this.grid.on('focusChange', function(ev) { - if (ev.rowKey !== null) { - var rowData = self.grid.getRow(ev.rowKey); - self.loadActnInfoToForm(rowData); - } - }); - - }, - /** * 조치정보 목록 조회 */ loadActnInfoList: function() { - if (this.grid) { - this.grid.readData(); + if (this.grid.instance) { + this.grid.instance.readData(); } }, @@ -398,24 +440,17 @@ if (response && response.data && response.data.length > 0) { $.each(response.data, function(index, photo) { var previewHtml = - '
' + + '
' + '
' + - ' ?actnInfoId=' + - photo.actnInfoId + '&crdnPhotoSn=' + photo.crdnPhotoSn + '" alt="' + photo.crdnPhotoNm + '">' + + ' ' + photo.orgnlPhotoNm + '' + '
' + '
' + - '
' + photo.crdnPhotoNm + '
' + - '
[조치]
' + - '
' + - ' ' + - ' ' + - '
' + + '
' + photo.orgnlPhotoNm + '
' + + '
[단속]
' + + ' ' + '
' + '
'; - $('#actnPhotoPreviewContainer').append(previewHtml); }); } @@ -471,14 +506,9 @@ contentType: false, success: function(response) { if (response && response.success) { - alert(mode === 'C' ? '조치정보가 등록되었습니다.' : '조치정보가 수정되었습니다.'); - - // 폼 초기화 - self.clearActnForm(); - // 그리드 새로고침 - if (self.grid) { - self.grid.readData(); + if (self.grid.instance) { + self.grid.instance.readData(); } } else { @@ -523,8 +553,8 @@ self.clearActnForm(); // 그리드 새로고침 - if (self.grid) { - self.grid.readData(); + if (self.grid.instance) { + self.grid.instance.readData(); } } else { @@ -554,7 +584,7 @@ $('#actnPhotoFiles').val(''); this.actnSelectedFiles = []; - self.grid.readData(); + self.grid.instance.readData(); console.log('조치정보 폼 초기화 완료'); }, @@ -644,7 +674,7 @@ '
' + '
' + file.name + '
' + '
[' + photoTypeName + ']
' + - ' ' + + ' ' + '
' + '
'; @@ -704,14 +734,6 @@ }); }, - /** - * 조치사진 원본 보기 (crdnActInfoRegistPopup.jsp의 viewOriginalActnPhoto 패턴) - */ - viewOriginalActnPhoto: function(actnInfoId, crdnPhotoSn) { - var url = '?actnInfoId=' + actnInfoId + '&crdnPhotoSn=' + crdnPhotoSn; - openPopup(url, 1000, 700, '_blank'); - }, - /** * 팝업 닫기 */ @@ -727,8 +749,17 @@ } }; + /** + * 원본 사진 보기 (기존 DB 사진) + * 중요한 로직 주석: 등록된 사진을 photoView 페이지에서 원본 크기로 표시하고 다운로드 버튼을 제공한다. + */ + function viewOriginalPhoto(actnInfoId, actInfoId, crdnPhotoSn, crdnPhotoSeCd) { + var url = '?actnInfoId=' + actnInfoId + '&actInfoId=' + actInfoId + '&crdnPhotoSn=' + crdnPhotoSn + '&crdnPhotoSeCd=' + crdnPhotoSeCd ; + openPopup(url, 1000, 700, '_blank'); + } + // DOM 준비 완료 시 초기화 $(document).ready(function() { - CrdnActnInfoManage.init(); + crdnActnInfoRegistPopup.init(); }); \ No newline at end of file