diff --git a/WebContent/WEB-INF/view/common/FileDetailPopup.jsp b/WebContent/WEB-INF/view/common/FileDetailPopup.jsp
index 74a224b..e9b09cf 100644
--- a/WebContent/WEB-INF/view/common/FileDetailPopup.jsp
+++ b/WebContent/WEB-INF/view/common/FileDetailPopup.jsp
@@ -102,19 +102,15 @@ function fn_fileCallback(areaId,fileType){
appendText +="
";
appendText +=" ";
*/
- appendText+= "";
- appendText+= " | "+[i+1]+" | ";
- appendText+= " "+data[i].REAL_FILE_NAME+"";
- /* if(data[i].WRITER=="${connectUserId}" || 'plm_admin'== "${connectUserId}"){
-
- appendText+= "";
-
- }
- */
- appendText+= " | ";
- appendText+= " "+data[i].REGDATE+" | "
- appendText+= " "+data[i].FILE_SIZE+" | "
- appendText+= "
";
+ appendText+= "";
+ appendText+= " | "+[i+1]+" | ";
+ appendText+= " "+data[i].REAL_FILE_NAME+"";
+ // 도면 삭제 버튼 활성화
+ appendText+= "";
+ appendText+= " | ";
+ appendText+= " "+data[i].REGDATE+" | "
+ appendText+= " "+data[i].FILE_SIZE+" | "
+ appendText+= "
";
}
//Swal.fire(appendText);
$("#"+areaId+"FileArea").append(appendText);
@@ -138,32 +134,57 @@ function fn_fileCallback(areaId,fileType){
/*첨부 파일 삭제 */
function fileDelete(fileObjId){
- if(confirm("파일을 삭제하시겠습니까?")){
- $.ajax({
- url:"/common/deleteFileInfo.do",
- type:"POST",
- data:{"objId":fileObjId},
- dataType:"json",
- async:true,
- success:function(data){
- fn_fileCallback("sr","${docType}");
- // 부모 창의 그리드 새로고침
- if(opener && typeof opener.fn_search == "function"){
- opener.fn_search();
- }
- // Tabulator 그리드가 있는 경우
- if(opener && opener._tabulGrid){
- opener._tabulGrid.replaceData();
- }
- // 기존 콜백 함수도 실행
- if(fnc_checkNull(callbackFnc) != ""){
- opener.eval(callbackFnc+"();");
- }
- },
- error: function(jqxhr, status, error){
- }
- });
- }
+ Swal.fire({
+ title: '파일을 삭제하시겠습니까?',
+ text: '삭제된 파일은 복구할 수 없습니다.',
+ icon: 'warning',
+ showCancelButton: true,
+ confirmButtonColor: '#3085d6',
+ cancelButtonColor: '#d33',
+ confirmButtonText: '삭제',
+ cancelButtonText: '취소',
+ reverseButtons: false
+ }).then(function(result) {
+ if (result.isConfirmed) {
+ $.ajax({
+ url:"/common/deleteFileInfo.do",
+ type:"POST",
+ data:{"objId":fileObjId},
+ dataType:"json",
+ async:true,
+ success:function(data){
+ Swal.fire({
+ title: '삭제 완료',
+ text: '파일이 삭제되었습니다.',
+ icon: 'success',
+ confirmButtonText: '확인'
+ }).then(function() {
+ fn_fileCallback("sr","${docType}");
+ // 부모 창의 그리드 새로고침
+ if(opener && typeof opener.fn_search == "function"){
+ opener.fn_search();
+ }
+ // Tabulator 그리드가 있는 경우
+ if(opener && opener._tabulGrid){
+ opener._tabulGrid.replaceData();
+ }
+ // 기존 콜백 함수도 실행
+ if(fnc_checkNull(callbackFnc) != ""){
+ opener.eval(callbackFnc+"();");
+ }
+ });
+ },
+ error: function(jqxhr, status, error){
+ Swal.fire({
+ title: '삭제 실패',
+ text: '파일 삭제 중 오류가 발생했습니다.',
+ icon: 'error',
+ confirmButtonText: '확인'
+ });
+ }
+ });
+ }
+ });
}
diff --git a/WebContent/WEB-INF/view/partMng/partMngList.jsp b/WebContent/WEB-INF/view/partMng/partMngList.jsp
index 2324081..44fe4ba 100644
--- a/WebContent/WEB-INF/view/partMng/partMngList.jsp
+++ b/WebContent/WEB-INF/view/partMng/partMngList.jsp
@@ -116,6 +116,16 @@ String connector = person.getUserId();
fn_centerPopup(popup_width, popup_height, url);
});
+ // 도면 다중 업로드 버튼 클릭
+ $("#btnDrawingUpload").click(function() {
+ $("#drawingFiles").click();
+ });
+
+ // 파일 선택 이벤트
+ $("#drawingFiles").change(function() {
+ fn_uploadDrawingFiles(this.files);
+ });
+
fn_search();
});
});
@@ -292,6 +302,174 @@ String connector = person.getUserId();
fn_centerPopup(popup_width, popup_height, url);
}
+ // 도면 다중 업로드 처리 함수
+ function fn_uploadDrawingFiles(files) {
+ if(!files || files.length === 0) {
+ Swal.fire('파일을 선택해주세요.');
+ return;
+ }
+
+ // 선택된 파트 확인 (필수 아님 - 전체 파트 대상)
+ var selectedParts = _tabulGrid.getSelectedData();
+ if(!selectedParts || selectedParts.length === 0) {
+ // 선택 없으면 전체 파트 대상으로 진행
+ var confirmMsg = '파트를 선택하지 않았습니다.\n';
+ confirmMsg += '전체 파트를 대상으로 파일명과 일치하는 품번에 업로드됩니다.\n';
+ confirmMsg += '계속하시겠습니까?';
+
+ if(!confirm(confirmMsg)) {
+ return;
+ }
+ }
+
+ // 파일 분류 및 처리
+ var filesByType = {
+ '3D': [], // stp 파일
+ '2D': [], // dwg 파일
+ 'PDF': [] // pdf 파일
+ };
+
+ // 파일 확장자 확인 및 분류
+ for(var i = 0; i < files.length; i++) {
+ var file = files[i];
+ var fileName = file.name;
+ var lastDotIndex = fileName.lastIndexOf('.');
+
+ if(lastDotIndex === -1) {
+ continue; // 확장자가 없는 파일은 스킵
+ }
+
+ var ext = fileName.substring(lastDotIndex + 1).toLowerCase();
+
+ if(ext === 'stp' || ext === 'step') {
+ filesByType['3D'].push(file);
+ } else if(ext === 'dwg') {
+ filesByType['2D'].push(file);
+ } else if(ext === 'pdf') {
+ filesByType['PDF'].push(file);
+ }
+ }
+
+ // 업로드할 파일이 있는지 확인
+ var totalFiles = filesByType['3D'].length + filesByType['2D'].length + filesByType['PDF'].length;
+ if(totalFiles === 0) {
+ Swal.fire('업로드 가능한 파일 형식이 없습니다. (stp, dwg, pdf만 가능)');
+ return;
+ }
+
+ // 확인 메시지
+ var msg = '총 ' + totalFiles + '개의 파일을 업로드하시겠습니까?\n';
+ msg += '- 3D (STP): ' + filesByType['3D'].length + '개\n';
+ msg += '- 2D (DWG): ' + filesByType['2D'].length + '개\n';
+ msg += '- PDF: ' + filesByType['PDF'].length + '개';
+
+ Swal.fire({
+ title: '도면 다중 업로드',
+ text: msg,
+ icon: 'question',
+ showCancelButton: true,
+ confirmButtonText: '업로드',
+ cancelButtonText: '취소'
+ }).then(function(result) {
+ if(result.isConfirmed) {
+ fn_processDrawingUpload(filesByType);
+ }
+ });
+ }
+
+ // 실제 업로드 처리
+ function fn_processDrawingUpload(filesByType) {
+ // 현재 그리드에 표시된 파트 데이터 가져오기
+ var gridData = _tabulGrid.getData();
+ if(!gridData || gridData.length === 0) {
+ Swal.fire('페이지에 표시된 파트가 없습니다.');
+ return;
+ }
+
+ // 품번 목록 생성 (현재 화면에 보이는 파트만)
+ var partNoList = [];
+ for(var i = 0; i < gridData.length; i++) {
+ var partNo = gridData[i].PART_NO;
+ if(partNo) {
+ partNoList.push(partNo);
+ }
+ }
+
+ // FormData 생성
+ var formData = new FormData();
+
+ // 현재 화면의 품번 목록 전송
+ formData.append('partNoList', JSON.stringify(partNoList));
+
+ // 모든 파일을 files 이름으로 추가
+ var allFiles = filesByType['3D'].concat(filesByType['2D']).concat(filesByType['PDF']);
+ for(var i = 0; i < allFiles.length; i++) {
+ formData.append('files', allFiles[i]);
+ }
+
+ // 로딩 표시
+ Swal.fire({
+ title: '업로드 중...',
+ text: '파일을 업로드하는 중입니다. 잠시만 기다려주세요.',
+ allowOutsideClick: false,
+ allowEscapeKey: false,
+ allowEnterKey: false,
+ showConfirmButton: false,
+ onOpen: function() {
+ Swal.showLoading();
+ }
+ });
+
+ // AJAX 업로드
+ $.ajax({
+ url: '/partMng/uploadDrawingFilesForPartList.do',
+ type: 'POST',
+ data: formData,
+ processData: false,
+ contentType: false,
+ success: function(response) {
+ Swal.close();
+
+ if(response.result === 'success') {
+ var successMsg = '도면 업로드가 완료되었습니다.\n\n';
+ successMsg += '- 성공: ' + response.successCount + '개\n';
+ if(response.failCount > 0) {
+ successMsg += '- 실패: ' + response.failCount + '개\n';
+ }
+ if(response.notFoundCount > 0) {
+ successMsg += '- 품번 미존재: ' + response.notFoundCount + '개\n';
+ }
+
+ Swal.fire({
+ title: '업로드 완료',
+ text: successMsg,
+ icon: response.failCount > 0 ? 'warning' : 'success'
+ }).then(function() {
+ // 그리드 새로고침
+ fn_search();
+ // 파일 input 초기화
+ $("#drawingFiles").val('');
+ });
+ } else {
+ Swal.fire({
+ title: '업로드 실패',
+ text: response.message || '도면 업로드 중 오류가 발생했습니다.',
+ icon: 'error'
+ });
+ }
+ },
+ error: function(xhr, status, error) {
+ Swal.close();
+ console.error('Upload error:', error);
+ Swal.fire({
+ title: '업로드 실패',
+ text: '서버 오류가 발생했습니다: ' + error,
+ icon: 'error'
+ });
+ }
+ });
+ }
+
@@ -314,8 +492,10 @@ String connector = person.getUserId();
+
+
diff --git a/WebContent/WEB-INF/view/partMng/partMngTempList.jsp b/WebContent/WEB-INF/view/partMng/partMngTempList.jsp
index 291e6c1..960734e 100644
--- a/WebContent/WEB-INF/view/partMng/partMngTempList.jsp
+++ b/WebContent/WEB-INF/view/partMng/partMngTempList.jsp
@@ -115,6 +115,16 @@ ui-jqgrid tr.jqgrow td {
});
});
+
+ // 도면 다중 업로드 버튼 클릭
+ $("#btnDrawingUpload").click(function() {
+ $("#drawingFiles").click();
+ });
+
+ // 파일 선택 이벤트
+ $("#drawingFiles").change(function() {
+ fn_uploadDrawingFiles(this.files);
+ });
});
@@ -410,6 +420,161 @@ ui-jqgrid tr.jqgrow td {
fn_centerPopup(popup_width, popup_height, url);
}
+ // 도면 다중 업로드 처리 함수
+ function fn_uploadDrawingFiles(files) {
+ if(!files || files.length === 0) {
+ Swal.fire('파일을 선택해주세요.');
+ return;
+ }
+
+ // 파일 분류 및 처리
+ var filesByType = {
+ '3D': [], // stp 파일
+ '2D': [], // dwg 파일
+ 'PDF': [] // pdf 파일
+ };
+
+ // 파일 확장자 확인 및 분류
+ for(var i = 0; i < files.length; i++) {
+ var file = files[i];
+ var fileName = file.name;
+ var lastDotIndex = fileName.lastIndexOf('.');
+
+ if(lastDotIndex === -1) {
+ continue; // 확장자가 없는 파일은 스킵
+ }
+
+ var ext = fileName.substring(lastDotIndex + 1).toLowerCase();
+
+ if(ext === 'stp' || ext === 'step') {
+ filesByType['3D'].push(file);
+ } else if(ext === 'dwg') {
+ filesByType['2D'].push(file);
+ } else if(ext === 'pdf') {
+ filesByType['PDF'].push(file);
+ }
+ }
+
+ // 업로드할 파일이 있는지 확인
+ var totalFiles = filesByType['3D'].length + filesByType['2D'].length + filesByType['PDF'].length;
+ if(totalFiles === 0) {
+ Swal.fire('업로드 가능한 파일 형식이 없습니다. (stp, dwg, pdf만 가능)');
+ return;
+ }
+
+ // 확인 메시지
+ var msg = '총 ' + totalFiles + '개의 파일을 업로드하시겠습니까?\n';
+ msg += '- 3D (STP): ' + filesByType['3D'].length + '개\n';
+ msg += '- 2D (DWG): ' + filesByType['2D'].length + '개\n';
+ msg += '- PDF: ' + filesByType['PDF'].length + '개';
+
+ Swal.fire({
+ title: '도면 다중 업로드',
+ text: msg,
+ icon: 'question',
+ showCancelButton: true,
+ confirmButtonText: '업로드',
+ cancelButtonText: '취소'
+ }).then(function(result) {
+ if(result.isConfirmed) {
+ fn_processDrawingUpload(filesByType);
+ }
+ });
+ }
+
+ // 실제 업로드 처리
+ function fn_processDrawingUpload(filesByType) {
+ // 현재 그리드에 표시된 파트 데이터 가져오기
+ var gridData = _tabulGrid.getData();
+ if(!gridData || gridData.length === 0) {
+ Swal.fire('페이지에 표시된 파트가 없습니다.');
+ return;
+ }
+
+ // 품번 목록 생성 (현재 화면에 보이는 파트만)
+ var partNoList = [];
+ for(var i = 0; i < gridData.length; i++) {
+ var partNo = gridData[i].PART_NO;
+ if(partNo) {
+ partNoList.push(partNo);
+ }
+ }
+
+ // FormData 생성
+ var formData = new FormData();
+
+ // 현재 화면의 품번 목록 전송
+ formData.append('partNoList', JSON.stringify(partNoList));
+
+ // 모든 파일을 files 이름으로 추가
+ var allFiles = filesByType['3D'].concat(filesByType['2D']).concat(filesByType['PDF']);
+ for(var i = 0; i < allFiles.length; i++) {
+ formData.append('files', allFiles[i]);
+ }
+
+ // 로딩 표시
+ Swal.fire({
+ title: '업로드 중...',
+ text: '파일을 업로드하는 중입니다. 잠시만 기다려주세요.',
+ allowOutsideClick: false,
+ allowEscapeKey: false,
+ allowEnterKey: false,
+ showConfirmButton: false,
+ onOpen: function() {
+ Swal.showLoading();
+ }
+ });
+
+ // AJAX 업로드
+ $.ajax({
+ url: '/partMng/uploadDrawingFilesForPartList.do',
+ type: 'POST',
+ data: formData,
+ processData: false,
+ contentType: false,
+ success: function(response) {
+ Swal.close();
+
+ if(response.result === 'success') {
+ var successMsg = '도면 업로드가 완료되었습니다.\n\n';
+ successMsg += '- 성공: ' + response.successCount + '개\n';
+ if(response.failCount > 0) {
+ successMsg += '- 실패: ' + response.failCount + '개\n';
+ }
+ if(response.notFoundCount > 0) {
+ successMsg += '- 품번 미존재: ' + response.notFoundCount + '개\n';
+ }
+
+ Swal.fire({
+ title: '업로드 완료',
+ text: successMsg,
+ icon: response.failCount > 0 ? 'warning' : 'success'
+ }).then(function() {
+ // 그리드 새로고침
+ fn_search();
+ // 파일 input 초기화
+ $("#drawingFiles").val('');
+ });
+ } else {
+ Swal.fire({
+ title: '업로드 실패',
+ text: response.message || '도면 업로드 중 오류가 발생했습니다.',
+ icon: 'error'
+ });
+ }
+ },
+ error: function(xhr, status, error) {
+ Swal.close();
+ console.error('Upload error:', error);
+ Swal.fire({
+ title: '업로드 실패',
+ text: '서버 오류가 발생했습니다: ' + error,
+ icon: 'error'
+ });
+ }
+ });
+ }
+
function openExcelPopup() {
/*
if($("#customer_cd").val()==""){
@@ -448,7 +613,9 @@ ui-jqgrid tr.jqgrow td {
+
+
diff --git a/WebContent/WEB-INF/view/partMng/structurePopupLeft.jsp b/WebContent/WEB-INF/view/partMng/structurePopupLeft.jsp
index 07c1745..6e58b9b 100644
--- a/WebContent/WEB-INF/view/partMng/structurePopupLeft.jsp
+++ b/WebContent/WEB-INF/view/partMng/structurePopupLeft.jsp
@@ -479,7 +479,7 @@ function fn_uploadDrawingFiles(files) {
msg += '- PDF: ' + filesByType['PDF'].length + '개';
Swal.fire({
- title: '도면 업로드',
+ title: '도면 다증 업로드',
text: msg,
icon: 'question',
showCancelButton: true,
@@ -596,7 +596,7 @@ function fn_processDrawingUpload(bomObjId, filesByType) {
-
+
diff --git a/src/com/pms/controller/PartMngController.java b/src/com/pms/controller/PartMngController.java
index 22a80e7..4515630 100644
--- a/src/com/pms/controller/PartMngController.java
+++ b/src/com/pms/controller/PartMngController.java
@@ -33,6 +33,9 @@ import com.pms.service.PartMgmtService;
import com.pms.service.PartMngService;
import com.pms.service.ProductMgmtService;
import com.pms.service.ProjectConceptService;
+import com.google.gson.Gson;
+import com.google.gson.reflect.TypeToken;
+import java.lang.reflect.Type;
@Controller
public class PartMngController {
@@ -2267,23 +2270,215 @@ public class PartMngController {
continue;
}
- // 파일명에서 품번 찾기 (파일명에 품번이 포함되어 있는 경우)
+ // 파일명에서 확장자 제거하여 품번 추출
+ String fileNameWithoutExt = originalFileName;
+ int lastDotIndex = originalFileName.lastIndexOf('.');
+ if(lastDotIndex > 0) {
+ fileNameWithoutExt = originalFileName.substring(0, lastDotIndex);
+ }
+
+ // 품번과 정확히 일치하는 경우만 매칭
String matchedPartNo = null;
- System.out.println("품번 매칭 시작 - 파일명: " + originalFileName);
- for(String partNo : partNoMap.keySet()) {
- System.out.println(" 품번 확인: " + partNo + " -> " + (originalFileName.contains(partNo) ? "매칭!" : "미매칭"));
- if(originalFileName.contains(partNo)) {
- matchedPartNo = partNo;
- System.out.println(" ✓ 품번 매칭 성공: " + partNo);
- break;
- }
+ System.out.println("품번 매칭 시작 - 파일명(확장자 제외): " + fileNameWithoutExt);
+
+ // 정확한 매칭 (품번과 파일명이 정확히 일치)
+ if(partNoMap.containsKey(fileNameWithoutExt)) {
+ matchedPartNo = fileNameWithoutExt;
+ System.out.println(" ✓ 품번 정확 매칭 성공: " + matchedPartNo);
}
if(matchedPartNo == null) {
- System.out.println(" ✗ 품번 매칭 실패 - 파일명에서 품번을 찾을 수 없음");
+ System.out.println(" ✗ 품번 매칭 실패 - 파일명과 일치하는 품번이 없음");
+ notFoundCount++;
+ continue;
+ }
+
+ // 해당 파트에 파일 정보 저장
+ Map partInfo = partNoMap.get(matchedPartNo);
+ String partObjId = CommonUtils.checkNull((String)partInfo.get("OBJID"));
+
+ Map fileMap = new HashMap<>();
+ fileMap.put("OBJID", CommonUtils.createObjId());
+ fileMap.put("TARGET_OBJID", partObjId);
+ fileMap.put("SAVED_FILE_NAME", savedFileName);
+ fileMap.put("REAL_FILE_NAME", originalFileName);
+ fileMap.put("DOC_TYPE", docType);
+ fileMap.put("DOC_TYPE_NAME", docTypeName);
+ fileMap.put("FILE_SIZE", String.valueOf(fileSize));
+ fileMap.put("FILE_EXT", fileExt);
+ fileMap.put("FILE_PATH", storagePath);
+ fileMap.put("WRITER", userId);
+
+ try {
+ // 파일 정보 DB 저장
+ partMngService.insertDrawingFile(fileMap);
+ successCount++;
+ } catch(Exception e) {
+ e.printStackTrace();
+ failCount++;
+ }
+ }
+ }
+
+ resultMap.put("result", "success");
+ resultMap.put("successCount", successCount);
+ resultMap.put("failCount", failCount);
+ resultMap.put("notFoundCount", notFoundCount);
+ resultMap.put("message", "업로드가 완료되었습니다.");
+
+ } catch(Exception e) {
+ e.printStackTrace();
+ resultMap.put("result", "error");
+ resultMap.put("message", "업로드 중 오류가 발생했습니다: " + e.getMessage());
+ } finally {
+ if(frc != null) {
+ frc.clear();
+ }
+ }
+
+ return resultMap;
+ }
+
+ /**
+ * PART 목록 화면에서 도면 파일 일괄 업로드
+ * BOM 정보 없이 전체 파트를 대상으로 파일명 매칭
+ *
+ * @param request
+ * @param session
+ * @return
+ */
+ @RequestMapping(value="/partMng/uploadDrawingFilesForPartList.do", method=RequestMethod.POST)
+ @ResponseBody
+ public Map uploadDrawingFilesForPartList(
+ HttpServletRequest request,
+ HttpSession session) {
+
+ Map resultMap = new HashMap<>();
+ MultipartRequest multi = null;
+ FileRenameClass frc = null;
+
+ try {
+ PersonBean person = (PersonBean)session.getAttribute(Constants.PERSON_BEAN);
+ String userId = person != null ? person.getUserId() : "plmAdmin";
+
+ // MultipartRequest로 파일 업로드 처리
+ String storagePath = Constants.FILE_STORAGE;
+ int maxSize = 1024*1024*1024*9; // 9GB
+
+ File storage = new File(storagePath);
+ if(!storage.exists()) storage.mkdirs();
+
+ frc = new FileRenameClass();
+ multi = new MultipartRequest(request, storagePath, maxSize, "UTF-8", frc);
+ java.util.List fileList = frc.getFileList();
+
+ // 화면에서 전달된 품번 목록 가져오기
+ String partNoListJson = multi.getParameter("partNoList");
+ if(partNoListJson == null || partNoListJson.isEmpty()) {
+ resultMap.put("result", "fail");
+ resultMap.put("message", "품번 목록이 전달되지 않았습니다.");
+ return resultMap;
+ }
+
+ // JSON 파싱 (Gson 사용)
+ Gson gson = new Gson();
+ Type listType = new TypeToken>(){}.getType();
+ java.util.List partNoList = gson.fromJson(partNoListJson, listType);
+
+ if(partNoList.isEmpty()) {
+ resultMap.put("result", "fail");
+ resultMap.put("message", "페이지에 표시된 파트가 없습니다.");
+ return resultMap;
+ }
+
+ System.out.println("========== 화면에서 전달된 품번 목록 ==========");
+ System.out.println("품번 개수: " + partNoList.size());
+ System.out.println("품번 목록: " + partNoList);
+ System.out.println("==========================================");
+
+ // 전달된 품번 목록에 해당하는 파트만 조회
+ Map partParam = new HashMap<>();
+ partParam.put("IS_LAST", "1");
+ partParam.put("PART_NO_LIST", partNoList);
+ ArrayList