이호영 이호영 2024-09-02
rowData[key] = typeof row[idx] === 'string' ? row[idx].trim() : row[idx]; // 문자열인지 확인하고 trim() 적용
row[idx]; // 문자열인지 확인하고 trim() 적용
@58357b78915e623f0005df61173572692e142ede
src/main/webapp/WEB-INF/jsp/web/addr/AddrList.jsp
--- src/main/webapp/WEB-INF/jsp/web/addr/AddrList.jsp
+++ src/main/webapp/WEB-INF/jsp/web/addr/AddrList.jsp
@@ -28,6 +28,16 @@
 // 	addrMassTab(1);	
 	
 	// 중복 휴대폰번호 버튼 노출여부
+	
+	
+	// excel 오류정보 테스트
+	$('#errorExcelBtn').click(function(){
+		if($tableError.getDataCount()<1){
+			alert('오류 정보가 없습니다.');
+			return false;
+		}
+		$tableError.download("xlsx", "error_data.xlsx");
+	});
 }); 
 
 // 메인 화면 좌측메뉴 최신화
@@ -213,7 +223,8 @@
 	//location.reload();
 	listAddrGrp();
 	addrGroupLoadAjax();
-	addrLoadAjax();			
+	addrLoadAjax();		
+	errorPopClean(); // 에러 팝업 초기화
 }
 
 // 주소록 그룹 중복체크
@@ -651,6 +662,7 @@
 
 <div id="adrPopup_tab1">
 
+
 <!-- 수신거부 대량등록  data-tooltip:adr_popup01 -->
 	<form id="addrMassForm" name="addrMassForm" method="post">
 		<input type="hidden" id="phoneList" name="phoneList" value=""/>
@@ -694,6 +706,39 @@
 			</div>
 		</div>
 	</form>
+	
+
+
+<!-- 주소록 상세 결과 팝업 data-tooltip:adr_popup14 -->
+	<div class="tooltip-wrap">
+		<div class="popup-com adr_layer adr_popup14" tabindex="0" data-tooltip-con="adr_popup14" data-focus="adr_popup14" data-focus-prev="adr_popu14-close" style="width: 450px;">
+			<div class="popup_heading">
+				<p>주소록 상세 결과</p>
+				<button type="button" class="tooltip-close" data-focus="adr_popup14-close"><img src="/publish/images/content/layerPopup_close.png" alt="팝업 닫기"></button>
+			</div>
+			<div class="layer_in" style="padding:30px 20px;">
+				<div class="table_top">
+					<p>
+						총 <span class="c_e40000" id="errorPopTotCnt">0</span>건 
+						/ 중복 <span class="c_002c9a" id="errorPopDupCnt">0</span>건
+						/ 오류 <span class="c_002c9a" id="errorPopErrorCnt">0</span>건</p>
+						<button type="button" class="excel_btn btnType" id="errorExcelBtn"><i class="downroad"></i>엑셀 다운로드</button>
+				</div>
+				<div class="tb_wrap adr_list" id="tabulator_error">
+				<!-- $tableError 참고  -->
+                </div>
+                <ul class="cf_text_ul">
+                    <li>*중복번호는 하나의 번호만 등록됩니다.</li>
+                    <li>*휴대폰 형식에 맞지 않는 데이터는 삭제 후 업로드 됩니다.</li>
+                    <li>ex) 발송불가 특수문자, 자릿수 오류 등</li>
+                </ul>
+                <div class="popup_btn_wrap2">
+                    <button type="button">저장</button>
+                    <button type="button" class="tooltip-close" data-focus="adr_popup14-close" data-focus-next="adr_popup14">닫기</button>                      
+                </div>
+			</div>
+		</div>
+	</div>
 
 </div>
 <!--// 주소록 대량등록 팝업 -->
src/main/webapp/WEB-INF/jsp/web/addr/include/addrListforExcel.jsp
--- src/main/webapp/WEB-INF/jsp/web/addr/include/addrListforExcel.jsp
+++ src/main/webapp/WEB-INF/jsp/web/addr/include/addrListforExcel.jsp
@@ -314,6 +314,7 @@
 //파일 드래그앤드롭 종료
 
 function excelFileChange(file) {
+	errorPopClean(); // 에러 popup 초기화
 	if (file) {
 		fn_loadAddActive();
 		var reader = new FileReader();
@@ -354,7 +355,8 @@
     data.forEach((row, index) => {
         var rowData = {};
         keys.forEach((key, idx) => { // index 변수명 변경 (내부와 외부에서 사용되므로 충돌 방지)
-            rowData[key] = row[idx] ? row[idx].trim() : ""; // 각 컬럼에 대해 기본값을 설정
+        	console.log('row[idx] : ', row[idx]);
+        	 rowData[key] = typeof row[idx] === 'string' ? row[idx].trim() : row[idx]; // 문자열인지 확인하고 trim() 적용
         });
         tableData.push(rowData);
 
@@ -495,6 +497,7 @@
 } */
 
 </script>
+
 	               <!-- 엑셀입력 -->
 	               <div class="popCont current pop_more_cont" id="popCont_1">
 	                   <div class="titBox">
@@ -549,13 +552,17 @@
 					</div>
 					<div class="excel_middle2">
 						<p>
-							총 <span class="c_e40000 fwBold" id="rowTotCnt">0</span>건 / 중복 <span class="c_002c9a fwBold" id="rowDupCnt">0</span>건
+							총 <span class="c_e40000 fwBold" id="rowTotCnt">0</span>건 
+							/ 중복 <span class="c_002c9a fwBold" id="rowDupCnt">0</span>건 
+							/ 오류 <span class="c_002c9a fwBold" id="rowErrorCnt">0</span>건 
+							<button type="button" class="btn_list_detail" data-tooltip="adr_popup14"><img src="/publish/images/search.png"></button>
+						</p>
+<!--                          <p>총 <span class="c_e40000">171</span>건 / 중복 <span class="c_002c9a">9</span>건 / 오류 <span class="c_002c9a">9</span>건 <button type="button" class="btn_list_detail"><img src="/publish/images/search.png"></button></p> -->
 							&nbsp; 
 <!-- 							<button type="button" class="btnType btnType6" data-tooltip="addrMassDupli_layer" onclick="GetAddrMassDupli()" id="btnAddrMassDupli">중복번호</button> -->
-							<button type="button" class="btnType btnType6" data-tooltip="addrMassDupli_layer" id="tableExcelDupliBtn">중복번호</button>
+<!-- 							<button type="button" class="btnType btnType6" data-tooltip="addrMassDupli_layer" id="tableExcelDupliBtn">중복번호</button> -->
 <!-- 							&nbsp; -->
 <!-- 							<button type="button" class="btnType btnType6" data-tooltip="addrMassSaveDupli_layer" onclick="GetAddrMassSaveDupli()" id="btnAddrMassSaveDupli">중복번호</button> -->
-						</p>
 <!-- 						<button type="button" class="btnType btnType6 addCallToF">번호추가</button> -->
 					</div>
 					
src/main/webapp/WEB-INF/jsp/web/msgdata/MsgDataSMLView.jsp
--- src/main/webapp/WEB-INF/jsp/web/msgdata/MsgDataSMLView.jsp
+++ src/main/webapp/WEB-INF/jsp/web/msgdata/MsgDataSMLView.jsp
@@ -7,6 +7,8 @@
 
 <script type="text/javascript" src="<c:url value='/publish/js/content.js'/>"></script>
 <script type="text/javascript" src="<c:url value='/js/txtSpecialReplace.js?date=202304250003'/>"></script>
+<!-- 주소록 유효성 체크 공통유틸로 인해 추가 -->
+<script type="text/javascript" src="<c:url value='/js/web/addr/event.js?date=202409021440'/>"></script>
 
 <% pageContext.setAttribute("newLineChar", "\r\n"); %>
 <script type="text/javascript">
@@ -1059,48 +1061,6 @@
 		
 	});
 	
-	// 유효한 번호인지 확인하는 함수
-	function isValidPhoneNumber(phone) {
-	    // 숫자만 추출
-	    const numberOnly = phone.replace(/\D/g, '');
-
-	    // 유효한 형식 체크
-	    return (
-	        (numberOnly.startsWith("010") && numberOnly.length === 11) ||    // 010으로 시작하고 11자리
-	        (/^01[1-9]/.test(numberOnly) && numberOnly.length === 10) ||    // 011~019로 시작하고 10자리
-	        (numberOnly.startsWith("050") && numberOnly.length === 12)      // 050X로 시작하고 12자리
-	    );
-	}
-
-	function formatPhoneNumber(phone) {
-	    // 숫자만 남기기
-	    let cleanedPhone = phone.replace(/\D/g, ''); // 모든 숫자가 아닌 문자 제거
-	    console.log('cleanedPhone : ', cleanedPhone);
-
-	    // 앞에 0이 추가된 경우 처리
-	    if (cleanedPhone.length === 10 && cleanedPhone.startsWith("10")) {
-	        // 10으로 시작하는 10자리 번호는 앞에 0을 추가하여 11자리로 만듦
-	        cleanedPhone = "0" + cleanedPhone;
-	    }else if (cleanedPhone.length === 9 && (cleanedPhone.startsWith("11") || cleanedPhone.startsWith("16") || cleanedPhone.startsWith("19"))) {
-	        // 11, 16, 19로 시작하는 9자리 번호는 앞에 0을 추가하여 10자리로 만듦
-	        cleanedPhone = "0" + cleanedPhone;
-	    }
-
-	    // 번호 형식 변환
-	    if (cleanedPhone.startsWith("010") && cleanedPhone.length === 11) {
-	        // 010-1234-5678 형식
-	        return cleanedPhone.substring(0, 3) + '-' + cleanedPhone.substring(3, 7) + '-' + cleanedPhone.substring(7);
-	    } else if ((/^01[1-9]/.test(cleanedPhone)) && cleanedPhone.length === 10) {
-	        // 01X-123-5678 형식
-	        return cleanedPhone.substring(0, 3) + '-' + cleanedPhone.substring(3, 6) + '-' + cleanedPhone.substring(6);
-	    } else if (cleanedPhone.startsWith("050") && cleanedPhone.length === 12) {
-	        // 050X-1234-5678 형식
-	        return cleanedPhone.substring(0, 4) + '-' + cleanedPhone.substring(4, 8) + '-' + cleanedPhone.substring(8);
-	    }
-
-		// 원본 반환 (표준 형식으로 변환되지 않으면)
-		return phone;
-	}
 
 
 
src/main/webapp/js/web/addr/event.js
--- src/main/webapp/js/web/addr/event.js
+++ src/main/webapp/js/web/addr/event.js
@@ -41,7 +41,7 @@
 		});
 
 
-		// 
+		// 데이터 넣기
 		updateTableFields($objTabul, group);
 		
 		// 필드가 휴대폰이면 열 중복체크
@@ -57,53 +57,136 @@
 	  * @ 핸드폰 중복 데이터
 	  * */
 	 function fn_phoneDupl($objTabul) {
-		    var data = $objTabul.getData();
-		    var uniquePhones = new Set();
-		    var dupliPhoneDataRealList = [];
-		    var rowsToKeep = [];
-		    var rowsToDelete = [];
-		    var phoneNumberChk = false;
+		 
+		$tableError.clearData();
+
+		var tabulNm = fn_utils_getTabulatorNm();
+		var tabluC = '.'+tabulNm
+		 
+		var data = $objTabul.getData();
+		var phoneNumberChk = false;
+	    var existingNumbers = new Set(); // 배열에서 Set으로 변경
+
+		console.log('입력된 번호들 : ', data);
 
 
-			var tabulNm = fn_utils_getTabulatorNm();
-			var tabluC = '.'+tabulNm
-		    
-		    data.forEach((row, index) => {
+		let errorCount = 0; // 중복 번호 개수를 저장할 변수
+		let duplicateCount = 0; // 중복 번호 개수를 저장할 변수
 
-		        if (!isValidKoreanPhoneNumber(row.addrPhoneNo)) {
-		        	phoneNumberChk = true
-		        	return false;
-		        }
-		    	
-		        if (uniquePhones.has(row.addrPhoneNo)) {
-		        	dupliPhoneDataRealList.push(row.addrPhoneNo);
-		            rowsToDelete.push(index); // 중복된 행의 인덱스를 기록
-		        } else {
-		            uniquePhones.add(row.addrPhoneNo);
-		            rowsToKeep.push(row); // 고유한 데이터만 추가
-		        }
-		    });
-		    
-		    if(phoneNumberChk){
-		    	alert('휴대폰 형식에 맞지 않는 데이터가 있습니다.\n 확인해 주세요');
-		    }
+		// 각 번호를 테이블에 추가 (중복 검사 및 포맷팅 포함)
+		for (let i = data.length - 1; i >= 0; i--) {
+//	    	console.log('index : ', index);
+//			console.log('existingNumbers : ', existingNumbers);
 
-		    $(tabluC+" #rowDupCnt").text(dupliPhoneDataRealList.length);
+		    let row = data[i];
+	    	var number = row.addrPhoneNo;
+			const formattedNumber = formatPhoneNumber(number); // 번호 표준화
+//		 			console.log('number : ', number)
+//		 			console.log('formattedNumber : ', formattedNumber)
+			const cleanedNumber = formattedNumber.replace(/[^0-9]/g, ''); // 숫자만 남김
+			if (!existingNumbers.has(cleanedNumber)) { // 중복 번호 체크
+				if (isValidPhoneNumber(formattedNumber)) { // 유효성 검사
+//					$tableError.addRow({ phone: formattedNumber }); // 표준화된 번호로 추가
+//					console.log('formattedNumber : ', formattedNumber);
+					
+//					console.log('통과 : ', row.addrPhoneNo, " : ", cleanedNumber);
+					row.addrPhoneNo = formattedNumber ;
+					existingNumbers.add(cleanedNumber); // 추가된 번호를 기존 목록에 추가
+				} else {
+					// 오류 : 유효성 통과 못함
+//					console.log('오류 : ', formattedNumber, ' : ',cleanedNumber);
+		            errorCount++;
+		            $tableError.addRow({ 
+		                name: row.addrNm, // 이름
+		                phone: row.addrPhoneNo, // 폰번호
+		                result: "오류" // 결과 메시지 추가
+		            });
+		            data.splice(i, 1); // 유효하지 않은 행 삭제
+				}
+			} else {
+				// 중복
+//				console.log('중복 : ', row.addrPhoneNo);
+				duplicateCount++; // 중복 번호가 발견될 때마다 카운트를 증가
+	            $tableError.addRow({ 
+	                name: row.addrNm, // 이름
+	                phone: row.addrPhoneNo, // 폰번호
+	                result: "중복" // 결과 메시지 추가
+	            });
+	            data.splice(i, 1); // 유효하지 않은 행 삭제
+			}
+		};
 
-		    if (dupliPhoneDataRealList.length > 0) {
+	    // 중복 카운트
+	    $(tabluC+" #rowDupCnt").text(duplicateCount);
+	    // 에러 카운트
+	    $(tabluC+" #rowErrorCnt").text(errorCount);
+
+	    // popup 영역
+	    // 중복 카운트
+	    $("#errorPopDupCnt").text(duplicateCount);
+	    // 에러 카운트
+	    $("#errorPopErrorCnt").text(errorCount);
+	    // 
+	    $("#errorPopTotCnt").text(duplicateCount+errorCount);
+	    
+		// 수정된 데이터로 테이블 업데이트
+		$objTabul.setData(data);
+
+		// 중복 번호 개수를 #rowDupCnt 요소에 표시
+		$("#rowDupCnt").text(duplicateCount);
+
+	    $(tabluC+" #rowTotCnt").text($objTabul.getDataCount());
+				
+	}
+	 
+	function fn_phoneDupl_old($objTabul) {
+	    var data = $objTabul.getData();
+	    var uniquePhones = new Set();
+	    var dupliPhoneDataRealList = [];
+	    var rowsToKeep = [];
+	    var rowsToDelete = [];
+	    var phoneNumberChk = false;
+
+
+		var tabulNm = fn_utils_getTabulatorNm();
+		var tabluC = '.'+tabulNm
+	    
+	    data.forEach((row, index) => {
+
+	        if (!isValidKoreanPhoneNumber(row.addrPhoneNo)) {
+	        	phoneNumberChk = true
+	        	return false;
+	        }
+	    	
+	        if (uniquePhones.has(row.addrPhoneNo)) {
+	        	dupliPhoneDataRealList.push(row.addrPhoneNo);
+	            rowsToDelete.push(index); // 중복된 행의 인덱스를 기록
+	        } else {
+	            uniquePhones.add(row.addrPhoneNo);
+	            rowsToKeep.push(row); // 고유한 데이터만 추가
+	        }
+	    });
+	    
+	    if(phoneNumberChk){
+	    	alert('휴대폰 형식에 맞지 않는 데이터가 있습니다.\n 확인해 주세요');
+	    }
+
+	    $(tabluC+" #rowDupCnt").text(dupliPhoneDataRealList.length);
+
+	    if (dupliPhoneDataRealList.length > 0) {
 //		        alert("중복된 휴대폰 번호가 있습니다: \n" + duplicatePhones.join(", "));
-		    	makeAddrMassDupliPop(dupliPhoneDataRealList);
-		    }
+	    	makeAddrMassDupliPop(dupliPhoneDataRealList);
+	    }
 
-		    // 중복된 행 삭제
-		    rowsToDelete.reverse().forEach(index => {
-		        $objTabul.deleteRow(index);
-		    });
+	    // 중복된 행 삭제
+	    rowsToDelete.reverse().forEach(index => {
+	        $objTabul.deleteRow(index);
+	    });
 
-		    // 고유한 데이터만 남기고 테이블 업데이트
-		    $objTabul.setData(rowsToKeep);
-		    $(tabluC+" #rowTotCnt").text(rowsToKeep.length);
-		}
+	    // 고유한 데이터만 남기고 테이블 업데이트
+	    $objTabul.setData(rowsToKeep);
+	    $(tabluC+" #rowTotCnt").text(rowsToKeep.length);
+	}
 
 		//받는사람 전체삭제 버튼 처리
 		$('.all_del').click(function(){
@@ -161,7 +244,10 @@
 	* 데이터테이블 필드값 수정                  
 	*/
 	function updateTableFields($objTabul, group) {
+		// 데이터가져오기
 		var currentData = $objTabul.getData();
+		
+		// 필드 초기 값 셋팅
 		var columns = [
 			{formatter: "rowSelection", titleFormatter: "rowSelection", clipboard: false, hozAlign: "center", headerHozAlign: "center", headerSort: false, cellClick: function(e, cell) {
 				cell.getRow().toggleSelect();
@@ -170,6 +256,7 @@
 		];
 
 		var fieldMapping = [];
+		// 초기 후 필드 값 셋팅하기
 		$('[data-group="' + group + '"] .field-selector').each(function(index) {
 			var selectedField = $(this).val();
 			//  ASCII 문자 코드 사용 - 65=A, 66=B ...
@@ -201,6 +288,7 @@
 			}
 		});
 
+		// 데이터 셋팅
 		var updatedData = currentData.map(row => {
 			var newRow = {};
 			fieldMapping.forEach((field, index) => {
@@ -258,12 +346,66 @@
 	$(showId+'DupliBtn').show()
 }
 
-//한국의 핸드폰 번호 형식 검사 함수
-function isValidKoreanPhoneNumber(phone) {
-    // 하이픈(-)을 제거하고 숫자만 남긴 후 검사
-    var cleaned = phone.replace(/-/g, '');
-    // 010, 011, 016, 017, 018, 019로 시작하고 10~11자리인 경우 유효
-    var valid = /^(010|011|016|017|018|019)\d{7,8}$/.test(cleaned);
-    return valid;
+
+
+
+// 유효한 번호인지 확인하는 함수
+function isValidPhoneNumber(phone) {
+    // 숫자만 추출
+    const numberOnly = phone.replace(/\D/g, '');
+
+    // 유효한 형식 체크
+    return (
+        (numberOnly.startsWith("010") && numberOnly.length === 11) ||    // 010으로 시작하고 11자리
+        (/^01[1-9]/.test(numberOnly) && numberOnly.length === 10) ||    // 011~019로 시작하고 10자리
+        (numberOnly.startsWith("050") && numberOnly.length === 12)      // 050X로 시작하고 12자리
+    );
 }
 
+// 유효성 후 포맷 맞추는 함수
+function formatPhoneNumber(phone) {
+    // 숫자만 남기기
+    let cleanedPhone = phone.replace(/\D/g, ''); // 모든 숫자가 아닌 문자 제거
+//    console.log('cleanedPhone : ', cleanedPhone);
+
+    // 앞에 0이 추가된 경우 처리
+    if (cleanedPhone.length === 10 && cleanedPhone.startsWith("10")) {
+        // 10으로 시작하는 10자리 번호는 앞에 0을 추가하여 11자리로 만듦
+        cleanedPhone = "0" + cleanedPhone;
+    }else if (cleanedPhone.length === 9 && (cleanedPhone.startsWith("11") || cleanedPhone.startsWith("16") || cleanedPhone.startsWith("19"))) {
+        // 11, 16, 19로 시작하는 9자리 번호는 앞에 0을 추가하여 10자리로 만듦
+        cleanedPhone = "0" + cleanedPhone;
+    }
+
+    // 번호 형식 변환
+    if (cleanedPhone.startsWith("010") && cleanedPhone.length === 11) {
+        // 010-1234-5678 형식
+        return cleanedPhone.substring(0, 3) + '-' + cleanedPhone.substring(3, 7) + '-' + cleanedPhone.substring(7);
+    } else if ((/^01[1-9]/.test(cleanedPhone)) && cleanedPhone.length === 10) {
+        // 01X-123-5678 형식
+        return cleanedPhone.substring(0, 3) + '-' + cleanedPhone.substring(3, 6) + '-' + cleanedPhone.substring(6);
+    } else if (cleanedPhone.startsWith("050") && cleanedPhone.length === 12) {
+        // 050X-1234-5678 형식
+        return cleanedPhone.substring(0, 4) + '-' + cleanedPhone.substring(4, 8) + '-' + cleanedPhone.substring(8);
+    }
+
+	// 원본 반환 (표준 형식으로 변환되지 않으면)
+	return phone;
+}
+
+// 주소록 에러결과 초기화
+function errorPopClean(){
+
+    // popup 영역
+	$tableError.clearData();
+    // 중복 카운트
+    $("#errorPopDupCnt").text(0);
+    // 에러 카운트
+    $("#errorPopErrorCnt").text(0);
+    // 
+    $("#errorPopTotCnt").text(0);
+}
+
+
+
+
src/main/webapp/js/web/addr/init.js
--- src/main/webapp/js/web/addr/init.js
+++ src/main/webapp/js/web/addr/init.js
@@ -18,6 +18,8 @@
 var $tableClip = null; //붙여넣기 탭
 var $tableSelf = null; //붙여넣기 탭
 
+var $tableError = null; //에러 팝업 영역
+
 $(document).ready(function(){
 
 	//Tabulator AJAX Data Loading
@@ -129,6 +131,34 @@
 	    },
 	});
 	
+
+	//Tabulator AJAX Data Loading
+	$tableError = new Tabulator("#tabulator_error", {
+		height:"255px",
+		width:"100%",
+	    layout:"fitColumns",
+	    autoColumns:false,
+	    headerHozAlign:"center", 
+	    validationMode:"highlight",
+	    clipboard:false,
+	    clipboardCopySelector:"table",
+	    clipboardPasteAction:"insert", // insert, update, replace
+	    placeholder:"Excel 파일을 업로드 해주세요.", //fit columns to width of table (optional)
+	 	columns:[ //Define Table Columns
+//	 		{formatter:"rowSelection", titleFormatter:"rowSelection",clipboard:false, hozAlign:"center", headerSort:false, cellClick:function(e, cell){
+//	 	        cell.getRow().toggleSelect();
+//		 		}
+//		 	}, 
+//			{formatter:"rownum", align:"center" ,title:"No", hozAlign:"center", headerHozAlign:"center", width:40},
+		 	{title:"이름", field:"name", hozAlign:"center", headerHozAlign: "center", width:125},
+		 	{title:"휴대폰", field:"phone", hozAlign:"center", headerHozAlign: "center", width:158},
+		 	{title:"미등록 결과", field:"result", hozAlign:"center", headerHozAlign: "center", width:125}
+//		 	{title:"이름", field:"name", hozAlign:"center", headerHozAlign: "center", width:125, validator:["maxLength:100", "string"]},
+//		 	{title:"휴대폰", field:"phone", hozAlign:"center", headerHozAlign: "center", width:125, validator:["maxLength:100", "string"]},
+//		 	{title:"미등록 결과", field:"result", hozAlign:"center", headerHozAlign: "center", width:125, validator:["maxLength:100", "string"]},
+	 	]
+	});
+
 	
 	
 	
Add a comment
List