불법행위 정보 - 조치에 대한 로직 재구성 진행 중...

dev
박성영 3 months ago
parent d0614645c8
commit 93a9b27825

@ -149,7 +149,8 @@ public class CrdnActInfoController {
@Parameter(description = "행위정보ID") @RequestParam String actInfoId,
@Parameter(description = "단속연도") @RequestParam String crdnYr,
@Parameter(description = "단속번호") @RequestParam String crdnNo,
@Parameter(description = "화면모드") @RequestParam(required = false, defaultValue = "V") String mode,
@Parameter(description = "위치정보ID") @RequestParam String pstnInfoId,
@Parameter(description = "화면모드") @RequestParam(required = false, defaultValue = "U") String mode,
Model model) {
log.debug("조치정보 관리 팝업 요청 - actInfoId: {}, crdnYr: {}, crdnNo: {}, mode: {}", actInfoId, crdnYr, crdnNo, mode);
@ -157,6 +158,7 @@ public class CrdnActInfoController {
ModelAndView mav = new ModelAndView("crdn/crndRegistAndView/crdnActInfo/crdnActnInfoManage" + TilesConstants.POPUP);
mav.addObject("mode", mode);
mav.addObject("actInfoId", actInfoId);
mav.addObject("pstnInfoId", pstnInfoId);
mav.addObject("crdnYr", crdnYr);
mav.addObject("crdnNo", crdnNo);

@ -26,11 +26,11 @@
<li id="tab-act" class="tab-item active" data-tab="act">
<a href="#" class="tab-link">1. 불법행위정보</a>
</li>
<%--<c:if test="${mode eq 'U'}">
<c:if test="${mode eq 'U'}">
<li id="tab-actn" class="tab-item" data-tab="actn">
<a href="#" class="tab-link">2. 조치정보</a>
</li>
</c:if>--%>
</c:if>
</ul>
</div>
@ -124,7 +124,7 @@
<span>단속 사진 선택</span>
<input type="file" id="crdnPhotoFiles" name="crdnPhotoFiles" accept="image/*" multiple style="display: none;">
</label>
<span class="file-info">최대 10개, 각 20MB 이하의 이미지 파일만 업로드 가능합니다.</span>
<span class="file-info">최대 10개, 각 10MB 이하의 이미지 파일만 업로드 가능합니다.</span>
</div>
<div class="photo-preview-container" id="crdnPhotoPreviewContainer">
<!-- 중요한 로직 주석: 수정 모드에서 기존 등록된 단속 사진들을 표시한다 -->
@ -153,76 +153,6 @@
</div>
</form>
</div>
<!-- 조치정보 탭 컨텐츠 -->
<div id="tab-content-actn" class="tab-content" style="display: none;">
<!-- 조치정보 그리드 -->
<div class="section-title mb-10">
<h3>조치정보 목록</h3>
<button type="button" id="btnAddActn" class="smallb-1 fr">조치정보 추가</button>
</div>
<div id="actnInfoGrid" style="height: 200px; margin-bottom: 20px;"></div>
<!-- 조치정보 입력 폼 -->
<div class="section-title mb-10">
<h3>조치정보 상세</h3>
</div>
<form id="actnInfoForm" name="actnInfoForm">
<input type="hidden" id="actnInfoId" name="actnInfoId" />
<input type="hidden" id="actnMode" name="actnMode" value="C" />
<div class="forms_table_non">
<table>
<colgroup>
<col style="width: 18%;" />
<col style="width: 32%;" />
<col style="width: 18%;" />
<col style="width: 32%;" />
</colgroup>
<tr>
<th class="th"><span class="required">*</span> 조치일자</th>
<td>
<input type="text" id="actnYmd" name="actnYmd" class="input calender datepicker" maxlength="10" style="width: 120px;" autocomplete="off" validation-check="required" />
</td>
<th class="th">조치 면적(㎡)</th>
<td>
<input type="text" id="actnArea" name="actnArea" class="input decimalMask" style="width: 120px;" maxlength="12" placeholder="예) 45.94" validation-check="number" />
</td>
</tr>
<tr>
<th class="th">비고</th>
<td colspan="3">
<textarea id="actnRmrk" name="actnRmrk" class="textarea" rows="3" maxlength="1000" style="height: 80px;"></textarea>
</td>
</tr>
<tr>
<th class="th">조치 사진</th>
<td colspan="3">
<!-- 조치 사진 업로드 영역 -->
<div class="file-upload-section" data-photo-type="2">
<div class="file-upload-controls">
<label for="actnPhotoFiles" class="file-upload-btn smallb-2">
<span>조치 사진 선택</span>
<input type="file" id="actnPhotoFiles" name="actnPhotoFiles" accept="image/*" multiple style="display: none;">
</label>
<span class="file-info">최대 10개, 각 20MB 이하의 이미지 파일만 업로드 가능합니다.</span>
</div>
<div class="photo-preview-container" id="actnPhotoPreviewContainer">
<!-- 기존 조치 사진들이 여기에 표시됩니다 -->
</div>
</div>
</td>
</tr>
</table>
</div>
</form>
<div class="section-foot mt-20">
<button type="button" id="btnSaveActn" class="newbtns bg1">조치정보 저장</button>
<button type="button" id="btnDeleteActn" class="newbtns bg3">조치정보 삭제</button>
<button type="button" id="btnClearActn" class="newbtns bg2">입력 초기화</button>
</div>
</div>
</div>
<div class="popup_foot">
@ -242,7 +172,7 @@
var mode = $('#mode').val();
// 달력 초기화
$('#actBgngYmd, #actnYmd').datepicker({
$('#actBgngYmd').datepicker({
container: '.popup_inner',
language: "kr"
});
@ -264,23 +194,6 @@
window.close();
});
// 조치정보 관련 버튼 이벤트
$('#btnAddActn').on('click', function() {
clearActnForm();
});
$('#btnSaveActn').on('click', function() {
saveActnInfo();
});
$('#btnDeleteActn').on('click', function() {
deleteActnInfo();
});
$('#btnClearActn').on('click', function() {
clearActnForm();
});
// 가로, 세로 입력 시 면적 자동 계산
// 중요한 로직 주석: 가로와 세로 값 입력이 완료되면(blur) 자동으로 면적을 계산하여 입력한다.
// input 이벤트 제거: inputmask와 충돌하여 정상 동작하지 않음
@ -297,17 +210,9 @@
// 파일 업로드 이벤트 초기화
initFileUpload();
// 조치정보 그리드 초기화 (수정 모드에서만)
if (mode === 'U') {
initActnInfoGrid();
}
console.log('불법행위정보 팝업이 초기화되었습니다. 모드:', mode);
});
// 조치정보 그리드 인스턴스
var actnInfoGrid;
var actnSelectedFiles = []; // 조치정보별 선택된 파일들
/**
* 탭 전환 함수
@ -319,21 +224,24 @@
var actInfoId = $('#actInfoId').val();
var crdnYr = $('#crdnYr').val();
var crdnNo = $('#crdnNo').val();
var pstnInfoId = $('#pstnInfoId').val();
if (!actInfoId) {
alert('행위정보가 등록되지 않았습니다. 먼저 불법행위정보를 저장해주세요.');
return;
}
var url = '<c:url value="/crdn/crndRegistAndView/crdnActInfo/crdnActnInfoManage.do"/>?mode=V' +
var url = '<c:url value="/crdn/crndRegistAndView/crdnActInfo/crdnActnInfoManage.do"/>?mode=U' +
'&actInfoId=' + encodeURIComponent(actInfoId) +
'&crdnYr=' + encodeURIComponent(crdnYr) +
'&pstnInfoId=' + encodeURIComponent(pstnInfoId) +
'&crdnNo=' + encodeURIComponent(crdnNo);
var newWindow = openPopup(url, 1400, 800, 'actnInfoManagePopup');
if (newWindow) {
// 메인페이지 opener 참조를 새 팝업에 전달
newWindow.mainOpener = window.opener;
window.close();
// 현재 창은 닫지 않고 유지 (사용자가 다시 돌아올 수 있도록)
} else {
alert('팝업이 차단되었습니다. 팝업 차단을 해제해주세요.');
@ -345,107 +253,6 @@
}
}
/**
* 조치정보 그리드 초기화
* 중요한 로직 주석: TUI Grid를 사용하여 조치정보 목록을 표시하고 행 클릭 시 하단 폼에 로드합니다.
*/
function initActnInfoGrid() {
var actInfoId = $('#actInfoId').val();
if (!actInfoId) {
console.warn('행위정보ID가 없어서 조치정보 그리드를 초기화할 수 없습니다.');
return;
}
actnInfoGrid = new XitTuiGridConfig({
el: document.getElementById('actnInfoGrid'),
data: {
api: {
readData: '<c:url value="/crdn/crndRegistAndView/crdnActnInfo/list.ajax"/>',
updateData: null,
deleteData: null,
createData: null
}
},
requestData: {
actInfoId: actInfoId // 행위정보ID로 조치정보 조회
},
columns: [
{
header: '조치정보ID',
name: 'actnInfoId',
hidden: true
},
{
header: '순번',
name: 'rownum',
width: 60,
align: 'center'
},
{
header: '조치일자',
name: 'actnYmd',
width: 100,
align: 'center',
formatter: function(value) {
if (value && value.length === 8) {
return value.substring(0,4) + '-' + value.substring(4,6) + '-' + value.substring(6,8);
}
return value || '-';
}
},
{
header: '조치면적(㎡)',
name: 'actnArea',
width: 100,
align: 'right',
formatter: function(value) {
return value ? parseFloat(value).toLocaleString() : '-';
}
},
{
header: '조치비고',
name: 'actnRmrk',
minWidth: 200,
formatter: function(value) {
return value || '-';
}
},
{
header: '등록일시',
name: 'regDt',
width: 130,
align: 'center',
formatter: function(value) {
if (value) {
return value.substring(0, 16).replace('T', ' ');
}
return '-';
}
}
],
pageOptions: {
useClient: true,
perPage: 5
},
rowHeaders: ['checkbox'],
onGridMounted: function(grid) {
// 그리드 로드 완료 후 첫 번째 행 선택
setTimeout(function() {
if (grid.getRowCount() > 0) {
grid.focus(0, 'actnYmd', true);
loadActnInfoToForm(grid.getRow(0));
}
}, 100);
},
onFocusChange: function(focusEvent) {
if (focusEvent.rowKey !== null) {
var rowData = actnInfoGrid.instance.getRow(focusEvent.rowKey);
loadActnInfoToForm(rowData);
}
}
});
}
// 드롭다운 인스턴스들
var actTypeCdDropdown;
var strctIdxDropdown;
@ -755,13 +562,6 @@
formData.append('crdnPhotoFiles', crdnSelectedFiles[i]);
}
}
// 중요한 로직 주석: 선택된 조치 사진들을 FormData에 추가 (있는 경우에만)
if (actnSelectedFiles && actnSelectedFiles.length > 0) {
for (var i = 0; i < actnSelectedFiles.length; i++) {
formData.append('actnPhotoFiles', actnSelectedFiles[i]);
}
}
var url = (mode === 'U') ?
'<c:url value="/crdn/crndRegistAndView/crdnActInfo/update.ajax"/>' :
@ -800,7 +600,6 @@
// 전역 변수: 선택된 파일들을 사진 종류별로 보관
var crdnSelectedFiles = []; // 단속 사진 파일들
var actnSelectedFiles = []; // 조치 사진 파일들
/**
* 파일 업로드 초기화
@ -816,11 +615,6 @@
handleFileSelect(e.target.files, '1');
});
// 조치 사진 파일 선택 이벤트
$('#actnPhotoFiles').on('change', function(e) {
handleFileSelect(e.target.files, '2');
});
// 중요로직: 단속 사진 드래그앤드롭 처리
crdnDropZone.on('dragover', function(e) {
e.preventDefault();
@ -844,30 +638,6 @@
handleFileSelect(files, '1'); // 단속 사진으로 처리
}
});
// 중요로직: 조치 사진 드래그앤드롭 처리
actnDropZone.on('dragover', function(e) {
e.preventDefault();
e.stopPropagation();
actnDropZone.addClass('drag-over');
});
actnDropZone.on('dragleave', function(e) {
e.preventDefault();
e.stopPropagation();
actnDropZone.removeClass('drag-over');
});
actnDropZone.on('drop', function(e) {
e.preventDefault();
e.stopPropagation();
actnDropZone.removeClass('drag-over');
var files = e.originalEvent.dataTransfer.files;
if (files.length > 0) {
handleFileSelect(files, '2'); // 조치 사진으로 처리
}
});
}
/**
@ -916,10 +686,10 @@
* 중요한 로직 주석: 파일 크기, 확장자, 타입을 검증한다.
*/
function validateFile(file) {
// 파일 크기 체크 (20MB)
var maxSize = 20 * 1024 * 1024;
// 파일 크기 체크 (10MB)
var maxSize = 10 * 1024 * 1024;
if (file.size > maxSize) {
alert(file.name + ' 파일이 20MB를 초과합니다.');
alert(file.name + ' 파일이 10MB를 초과합니다.');
return false;
}
@ -1036,294 +806,4 @@
openPopup(url, 1000, 700, '_blank');
}
/**
* 조치정보 그리드 행 클릭 이벤트 핸들러
* 중요한 로직 주석: 그리드에서 선택된 조치정보 데이터를 하단 폼에 로드한다.
*/
function onActnInfoGridClick(ev) {
var rowData = ev.rowKey !== null ? actnInfoGrid.getRow(ev.rowKey) : null;
if (rowData) {
loadActnInfoToForm(rowData);
}
}
/**
* 조치정보 데이터를 폼에 로드
* 중요한 로직 주석: 선택된 조치정보 데이터를 하단 입력 폼의 각 필드에 설정한다.
* @param {Object} rowData - 그리드에서 선택된 조치정보 데이터
*/
function loadActnInfoToForm(rowData) {
// 조치정보 ID 설정 (숨겨진 필드)
$('#actnInfoId').val(rowData.actnInfoId || '');
// 조치 일자 설정
$('#actnYmd').val(rowData.actnYmd || '');
// 조치 면적 설정 (소수점 2자리까지 표시)
$('#actnArea').val(rowData.actnArea || '');
// 조치 비고 설정
$('#actnRmrk').val(rowData.actnRmrk || '');
// 폼 모드를 수정으로 설정
$('#actnFormMode').val('U');
// 기존 조치사진 로드
loadActnPhotos(rowData.actnInfoId);
log.debug('조치정보 폼 로드 완료', rowData);
}
/**
* 조치사진 목록 로드
* 중요한 로직 주석: 선택된 조치정보의 기존 조치사진들을 조회하여 미리보기 영역에 표시한다.
* @param {string} actnInfoId - 조치정보 ID
*/
function loadActnPhotos(actnInfoId) {
if (!actnInfoId) {
$('#actnPhotoPreviewContainer').empty();
return;
}
$.ajax({
url: '<c:url value="/crdn/crndRegistAndView/crdnActInfo/selectActnPhotos.ajax"/>',
type: 'GET',
data: { actnInfoId: actnInfoId },
success: function(response) {
$('#actnPhotoPreviewContainer').empty();
if (response && response.data && response.data.length > 0) {
$.each(response.data, function(index, photo) {
var previewHtml =
'<div class="photo-preview-item existing-photo" data-actn-info-id="' + photo.actnInfoId +
'" data-photo-sn="' + photo.crdnPhotoSn + '" data-photo-type="2">' +
' <div class="photo-thumbnail">' +
' <img src="<c:url value="/crdn/crndRegistAndView/crdnActInfo/photoThumbnail.do"/>?actnInfoId=' +
photo.actnInfoId + '&crdnPhotoSn=' + photo.crdnPhotoSn + '" alt="' + photo.crdnPhotoNm + '">' +
' </div>' +
' <div class="photo-info">' +
' <div class="photo-name" title="' + photo.crdnPhotoNm + '">' + photo.crdnPhotoNm + '</div>' +
' <div class="photo-type">[조치]</div>' +
' <div class="photo-buttons">' +
' <button type="button" class="view-photo-btn" onclick="viewOriginalActnPhoto(\'' +
photo.actnInfoId + '\', \'' + photo.crdnPhotoSn + '\')">보기</button>' +
' <button type="button" class="delete-photo-btn" onclick="deleteExistingActnPhoto(\'' +
photo.actnInfoId + '\', \'' + photo.crdnPhotoSn + '\')">삭제</button>' +
' </div>' +
' </div>' +
'</div>';
$('#actnPhotoPreviewContainer').append(previewHtml);
});
}
},
error: function() {
alert('조치사진 조회 중 오류가 발생했습니다.');
}
});
}
/**
* 조치정보 저장
* 중요한 로직 주석: 입력된 조치정보와 조치사진을 서버에 저장한다. 신규등록/수정을 자동 판단한다.
*/
function saveActnInfo() {
// 기본 유효성 검증
if (!validateActnForm()) {
return;
}
var actnInfoId = $('#actnInfoId').val().trim();
var actInfoId = $('#actInfoId').val(); // 부모 행위정보 ID
var mode = actnInfoId ? 'U' : 'C'; // 조치정보 ID가 있으면 수정, 없으면 신규
// FormData 생성 (파일 업로드를 위해)
var formData = new FormData();
formData.append('mode', mode);
formData.append('actInfoId', actInfoId); // 부모 행위정보 ID
formData.append('actnInfoId', actnInfoId);
formData.append('actnYmd', $('#actnYmd').val().trim());
formData.append('actnArea', $('#actnArea').val().trim());
formData.append('actnRmrk', $('#actnRmrk').val().trim());
// 새로 선택한 조치사진 파일들 추가
if (actnSelectedFiles && actnSelectedFiles.length > 0) {
for (var i = 0; i < actnSelectedFiles.length; i++) {
formData.append('actnPhotoFiles', actnSelectedFiles[i]);
}
}
// 저장 요청
$.ajax({
url: '<c:url value="/crdn/crndRegistAndView/crdnActInfo/saveActnInfo.ajax"/>',
type: 'POST',
data: formData,
processData: false,
contentType: false,
success: function(response) {
if (response && response.success) {
alert(mode === 'C' ? '조치정보가 등록되었습니다.' : '조치정보가 수정되었습니다.');
// 폼 초기화
clearActnForm();
// 그리드 새로고침
actnInfoGrid.readData(1);
} else {
alert(response.message || '조치정보 저장에 실패했습니다.');
}
},
error: function() {
alert('조치정보 저장 중 오류가 발생했습니다.');
}
});
}
/**
* 조치정보 삭제
* 중요한 로직 주석: 선택된 조치정보와 관련 조치사진을 논리삭제한다.
*/
function deleteActnInfo() {
var actnInfoId = $('#actnInfoId').val().trim();
if (!actnInfoId) {
alert('삭제할 조치정보를 선택해주세요.');
return;
}
if (!confirm('선택한 조치정보를 삭제하시겠습니까?\n관련된 조치사진도 함께 삭제됩니다.')) {
return;
}
$.ajax({
url: '<c:url value="/crdn/crndRegistAndView/crdnActInfo/deleteActnInfo.ajax"/>',
type: 'POST',
data: {
actnInfoId: actnInfoId,
mode: 'D'
},
success: function(response) {
if (response && response.success) {
alert('조치정보가 삭제되었습니다.');
// 폼 초기화
clearActnForm();
// 그리드 새로고침
actnInfoGrid.readData(1);
} else {
alert(response.message || '조치정보 삭제에 실패했습니다.');
}
},
error: function() {
alert('조치정보 삭제 중 오류가 발생했습니다.');
}
});
}
/**
* 조치정보 폼 초기화
* 중요한 로직 주석: 조치정보 입력 폼의 모든 필드를 초기화하고 신규등록 모드로 설정한다.
*/
function clearActnForm() {
$('#actnInfoId').val('');
$('#actnYmd').val('');
$('#actnArea').val('');
$('#actnRmrk').val('');
$('#actnFormMode').val('C');
// 조치사진 초기화
$('#actnPhotoPreviewContainer').empty();
$('#actnPhotoFiles').val('');
actnSelectedFiles = [];
log.debug('조치정보 폼 초기화 완료');
}
/**
* 조치정보 폼 유효성 검증
* 중요한 로직 주석: 조치정보 입력값의 필수 항목과 형식을 검증한다.
* @returns {boolean} 유효성 검증 결과
*/
function validateActnForm() {
var actnYmd = $('#actnYmd').val().trim();
var actnArea = $('#actnArea').val().trim();
// 조치 일자 필수 입력 체크
if (!actnYmd) {
alert('조치 일자를 입력해주세요.');
$('#actnYmd').focus();
return false;
}
// 조치 일자 형식 체크 (YYYYMMDD)
var dateRegex = /^\d{8}$/;
if (!dateRegex.test(actnYmd)) {
alert('조치 일자는 YYYYMMDD 형식으로 입력해주세요.');
$('#actnYmd').focus();
return false;
}
// 조치 면적 형식 체크 (숫자, 소수점 허용)
if (actnArea && isNaN(parseFloat(actnArea))) {
alert('조치 면적은 숫자로 입력해주세요.');
$('#actnArea').focus();
return false;
}
// 조치 면적 범위 체크 (0보다 큰 값)
if (actnArea && parseFloat(actnArea) <= 0) {
alert('조치 면적은 0보다 큰 값을 입력해주세요.');
$('#actnArea').focus();
return false;
}
return true;
}
/**
* 기존 조치사진 삭제
* 중요한 로직 주석: DB에 등록된 조치사진을 논리삭제하고 화면에서 제거한다.
* @param {string} actnInfoId - 조치정보 ID
* @param {string} crdnPhotoSn - 사진 순번
*/
function deleteExistingActnPhoto(actnInfoId, crdnPhotoSn) {
if (!confirm('선택한 조치사진을 삭제하시겠습니까?')) {
return;
}
$.ajax({
url: '<c:url value="/crdn/crndRegistAndView/crdnActInfo/deleteActnPhoto.ajax"/>',
type: 'POST',
data: {
actnInfoId: actnInfoId,
crdnPhotoSn: crdnPhotoSn
},
success: function(response) {
if (response && response.success) {
// 화면에서 해당 사진 제거
$('[data-actn-info-id="' + actnInfoId + '"][data-photo-sn="' + crdnPhotoSn + '"][data-photo-type="2"]').remove();
alert('조치사진이 삭제되었습니다.');
} else {
alert(response.message || '조치사진 삭제에 실패했습니다.');
}
},
error: function() {
alert('조치사진 삭제 중 오류가 발생했습니다.');
}
});
}
/**
* 조치사진 원본 보기
* 중요한 로직 주석: 등록된 조치사진을 팝업 창에서 원본 크기로 표시한다.
*/
function viewOriginalActnPhoto(actnInfoId, crdnPhotoSn) {
var url = '<c:url value="/crdn/crndRegistAndView/crdnActInfo/actnPhotoView.do"/>?actnInfoId=' + actnInfoId + '&crdnPhotoSn=' + crdnPhotoSn;
openPopup(url, 1000, 700, '_blank');
}
</script>
Loading…
Cancel
Save