Files
vexplor/WebContent/WEB-INF/view/contractMgmt/contracMgmtdetailFormPopup.jsp
2025-08-21 09:41:46 +09:00

1984 lines
58 KiB
Plaintext

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="com.pms.common.utils.*"%>
<%@ page import="java.util.*"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@include file="/init.jsp"%>
<%
PersonBean person = (PersonBean) session.getAttribute(Constants.PERSON_BEAN);
String userId = CommonUtils.checkNull(person.getUserId());
%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title><%=Constants.SYSTEM_NAME%></title>
<style>
.fileListscrollTbody td {
font-size: 11px !important;
}
/* 컴팩트한 첨부파일 영역 디자인 - 높이 최적화 */
.file_upload_main_table {
width: 100%;
border-collapse: collapse;
table-layout: fixed;
}
.file_upload_main_table td {
width: 50%;
vertical-align: top;
padding: 8px; /* 패딩을 줄여서 더 컴팩트하게 */
}
/* 각 첨부파일 섹션의 전체 컨테이너 - 높이 최적화 */
.file_section_container {
border: 1px solid #ddd; /* 테두리를 얇게 해서 더 깔끔하게 */
border-radius: 6px;
background: #f8f9fa;
overflow: hidden;
height: 200px; /* 전체 높이를 200px로 제한 */
}
/* 섹션 제목 영역 - 높이 줄임 */
.file_section_header {
background: #6c757d;
color: white;
text-align: center;
padding: 8px; /* 패딩을 줄여서 헤더 높이 감소 */
font-weight: bold;
font-size: 13px; /* 폰트 크기도 약간 줄임 */
margin: 0;
}
/* 드래그 앤 드롭 영역의 컨테이너 - 패딩 줄임 */
.drag_drop_container {
padding: 12px; /* 패딩을 대폭 줄임 */
background: white;
}
/* 실제 드래그 앤 드롭 영역 - 높이 대폭 감소 */
.drag_drop_zone {
border: 2px dashed #ccc;
border-radius: 6px;
padding: 15px 10px; /* 패딩을 줄여서 높이 감소 */
text-align: center;
background: #fafafa;
cursor: pointer;
transition: all 0.3s ease;
min-height: 40px; /* 최소 높이를 40px로 대폭 줄임 */
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.drag_drop_zone:hover,
.drag_drop_zone.dragover {
border-color: #28a745;
background: #f8fff8;
color: #28a745;
}
/* 드래그 앤 드롭 텍스트 스타일 - 크기 줄임 */
.drag_drop_text {
font-size: 13px; /* 폰트 크기 줄임 */
font-weight: bold;
color: #666;
margin-bottom: 4px; /* 마진 줄임 */
}
.drag_drop_hint {
font-size: 10px; /* 힌트 텍스트 크기 더욱 줄임 */
color: #999;
}
/* 파일 목록 영역 */
.file_list_section {
background: white;
border-top: 1px solid #ddd;
flex: 1; /* 남은 공간을 모두 차지하도록 설정 */
display: flex;
flex-direction: column;
}
.file_list_header {
background: #e9ecef;
padding: 6px; /* 패딩을 줄여서 헤더 높이 감소 */
font-weight: bold;
text-align: center;
border-bottom: 1px solid #ddd;
font-size: 12px; /* 폰트 크기 줄임 */
}
/* 파일 목록 컨테이너 - 높이 최적화 */
.file_list_content {
max-height: 80px; /* 최대 높이를 80px로 줄임 */
overflow-y: auto;
min-height: 50px; /* 최소 높이도 줄임 */
flex: 1; /* 남은 공간을 모두 사용 */
}
.file_list_content table {
width: 100%;
border-collapse: collapse;
}
.file_list_content td {
padding: 6px 8px; /* 패딩을 줄여서 행 높이 감소 */
border-bottom: 1px solid #f0f0f0;
font-size: 11px; /* 폰트 크기 줄임 */
line-height: 1.2; /* 줄 간격 줄임 */
}
.file_list_content tr:hover {
background: #f8f9fa;
}
/* 파일 링크 스타일 */
.file_link {
color: #007bff;
text-decoration: none;
display: inline-block;
max-width: 150px; /* 최대 너비를 줄임 */
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.file_link:hover {
text-decoration: underline;
color: #0056b3;
}
/* 파일 삭제 버튼 */
.delete_btn {
width: 14px; /* 크기를 줄임 */
height: 14px;
background: #dc3545;
border-radius: 50%;
cursor: pointer;
margin-left: 6px; /* 마진 줄임 */
display: inline-block;
position: relative;
vertical-align: middle;
transition: background 0.2s ease;
}
.delete_btn:hover {
background: #c82333;
}
/* X 표시 구현 */
.delete_btn:before,
.delete_btn:after {
content: '';
position: absolute;
top: 50%;
left: 50%;
width: 6px; /* 크기 줄임 */
height: 1px;
background: white;
transform: translate(-50%, -50%) rotate(45deg);
}
.delete_btn:after {
transform: translate(-50%, -50%) rotate(-45deg);
}
/* 빈 파일 목록 메시지 */
.empty_file_message {
text-align: center;
color: #999;
padding: 15px 10px; /* 패딩 줄임 */
font-style: italic;
font-size: 11px; /* 폰트 크기 줄임 */
}
/* 스크롤바 스타일 - 더 얇게 */
.file_list_content::-webkit-scrollbar {
width: 4px; /* 스크롤바 너비 줄임 */
}
.file_list_content::-webkit-scrollbar-track {
background: #f1f1f1;
border-radius: 2px;
}
.file_list_content::-webkit-scrollbar-thumb {
background: #c1c1c1;
border-radius: 2px;
}
.file_list_content::-webkit-scrollbar-thumb:hover {
background: #a8a8a8;
}
/* ======================================= */
/* 이미지와 동일한 간단한 파일첨부 영역 스타일 */
/* ======================================= */
/* 파일 섹션 헤더 셀 - 회색 배경으로 TD 영역에 꽉참 */
.file_section_header_cell {
background: #6c757d !important;
color: white !important;
text-align: center !important;
font-weight: bold !important;
font-size: 13px !important;
vertical-align: middle !important;
padding: 10px !important;
}
/* 드래그 앤 드롭 셀 */
.file_drag_drop_cell {
padding: 8px !important;
vertical-align: middle !important;
}
/* 간단한 드래그 앤 드롭 영역 */
.drag_drop_zone_simple {
border: 2px dashed #ccc;
border-radius: 6px;
padding: 20px 10px;
text-align: center;
background: #fafafa;
cursor: pointer;
transition: all 0.3s ease;
height: 60px;
display: flex;
align-items: center;
justify-content: center;
}
.drag_drop_zone_simple:hover,
.drag_drop_zone_simple.dragover {
border-color: #28a745;
background: #f8fff8;
color: #28a745;
}
.drag_drop_zone_simple .drag_drop_text {
font-size: 12px;
font-weight: bold;
color: #666;
}
/* 파일 목록 셀 */
.file_list_cell {
padding: 8px !important;
vertical-align: middle !important;
}
/* 간단한 파일 목록 내용 */
.file_list_content_simple {
max-height: 80px;
overflow-y: auto;
border: 1px solid #ddd;
border-radius: 4px;
background: white;
}
.file_list_content_simple table {
width: 100%;
border-collapse: collapse;
}
.file_list_content_simple td {
padding: 6px 10px;
border-bottom: 1px solid #f0f0f0;
font-size: 11px;
line-height: 1.2;
}
.file_list_content_simple tr:hover {
background: #f8f9fa;
}
.file_list_content_simple tr:last-child td {
border-bottom: none;
}
/* 파일 링크 스타일 */
.file_list_content_simple .file_link {
color: #007bff;
text-decoration: none;
display: inline-block;
max-width: 200px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.file_list_content_simple .file_link:hover {
text-decoration: underline;
color: #0056b3;
}
/* 삭제 버튼 */
.file_list_content_simple .delete_btn {
width: 14px;
height: 14px;
background: #dc3545;
border-radius: 50%;
cursor: pointer;
margin-left: 6px;
display: inline-block;
position: relative;
vertical-align: middle;
transition: background 0.2s ease;
}
.file_list_content_simple .delete_btn:hover {
background: #c82333;
}
/* X 표시 구현 */
.file_list_content_simple .delete_btn:before,
.file_list_content_simple .delete_btn:after {
content: '';
position: absolute;
top: 50%;
left: 50%;
width: 6px;
height: 1px;
background: white;
transform: translate(-50%, -50%) rotate(45deg);
}
.file_list_content_simple .delete_btn:after {
transform: translate(-50%, -50%) rotate(-45deg);
}
/* 빈 파일 목록 메시지 */
.file_list_content_simple .empty_file_message {
text-align: center;
color: #999;
padding: 15px 10px;
font-style: italic;
font-size: 11px;
}
/* 스크롤바 스타일 */
.file_list_content_simple::-webkit-scrollbar {
width: 4px;
}
.file_list_content_simple::-webkit-scrollbar-track {
background: #f1f1f1;
border-radius: 2px;
}
.file_list_content_simple::-webkit-scrollbar-thumb {
background: #c1c1c1;
border-radius: 2px;
}
.file_list_content_simple::-webkit-scrollbar-thumb:hover {
background: #a8a8a8;
}
/* ======================================= */
/* 견적이력관리 전용 스타일 */
/* ======================================= */
/* 견적 관리 테이블 스타일 */
#quoteManagementTable {
border-collapse: collapse;
width: 100%;
}
#quoteManagementTable td {
border: 1px solid #ddd;
padding: 8px;
vertical-align: middle;
}
#quoteManagementTable .quote_input,
#quoteManagementTable .quote_select {
width: 100%;
border: 1px solid #ccc;
padding: 4px;
font-size: 12px;
box-sizing: border-box;
}
/* 견적서 첨부파일 드롭존 */
.quote_dropzone {
border: 2px dashed #ccc;
border-radius: 4px;
padding: 8px;
text-align: center;
background: #fafafa;
cursor: pointer;
transition: all 0.3s ease;
min-height: 30px;
font-size: 11px;
color: #666;
}
.quote_dropzone:hover,
.quote_dropzone.dragover {
border-color: #28a745;
background: #f8fff8;
color: #28a745;
}
.quote_file_list {
max-height: 50px;
overflow-y: auto;
border: 1px solid #ddd;
background: white;
margin-top: 4px;
}
.quote_file_item {
padding: 4px 6px;
border-bottom: 1px solid #f0f0f0;
font-size: 10px;
display: flex;
justify-content: space-between;
align-items: center;
}
.quote_file_item:last-child {
border-bottom: none;
}
.quote_file_link {
color: #007bff;
text-decoration: none;
flex: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.quote_file_link:hover {
text-decoration: underline;
}
.quote_delete_btn {
width: 14px;
height: 14px;
background: #dc3545;
border-radius: 50%;
cursor: pointer;
position: relative;
transition: background 0.2s ease;
margin-left: 4px;
}
.quote_delete_btn:hover {
background: #c82333;
}
.quote_delete_btn:before,
.quote_delete_btn:after {
content: '';
position: absolute;
top: 50%;
left: 50%;
width: 6px;
height: 1px;
background: white;
transform: translate(-50%, -50%) rotate(45deg);
}
.quote_delete_btn:after {
transform: translate(-50%, -50%) rotate(-45deg);
}
</style>
<script type="text/javascript">
// ===================================================================
// 1. 전역 변수 선언부 - 페이지 전체에서 사용되는 변수들
// ===================================================================
// 견적 관리 관련 전역 변수
var quoteRowCounter = ${empty detailList ? 0 : detailList.size()}; // JSTL로 기존 데이터 개수 초기화
var currencyTypeOptions = ''; // 통화 옵션 HTML 문자열 저장용
// ===================================================================
// 2. 공통 유틸리티 함수들 - 없을 경우를 대비한 안전한 함수 정의
// ===================================================================
// 숫자에 천단위 콤마 추가 함수
if (typeof addComma !== 'function') {
function addComma(data) {
if (!data) return '';
return data.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
}
}
// null 체크 함수
if (typeof fnc_checkNull !== 'function') {
function fnc_checkNull(value) {
if (value === null || value === undefined || value === 'null' || value === 'undefined') {
return '';
}
return value;
}
}
// 고유 ID 생성 함수
if (typeof fnc_createObjId !== 'function') {
function fnc_createObjId() {
return 'OBJ_' + new Date().getTime() + '_' + Math.random().toString(36).substr(2, 9);
}
}
// ===================================================================
// 3. 메인 초기화 함수 - 페이지 로드 시 실행되는 모든 초기화 작업
// ===================================================================
$(function() {
$("#form1 input[type='text'], #form1 select , input[type='number']").prop("disabled", true);
// -----------------------------------------------------------
// 3-1. 통화 옵션 초기화 (견적 관리에서 사용)
// -----------------------------------------------------------
currencyTypeOptions =
'<option value="">선택</option>' +
'<option value="KRW">원화(KRW)</option>' +
'<option value="USD">달러(USD)</option>' +
'<option value="EUR">유로(EUR)</option>' +
'<option value="JPY">엔화(JPY)</option>' +
'<option value="CNY">위안화(CNY)</option>';
// -----------------------------------------------------------
// 3-2. 기본 폼 요소 초기화 및 계산 로직
// -----------------------------------------------------------
// 견적금액과 설비대수 변경 시 수주가 자동계산
$("#quote_amount_1st, #quote_amount_2nd, #quote_amount_3rd, #facility_qty").on("keyup change", function() {
calculateOrderAmount();
});
// 계약구분에 따른 화면 표시/숨김 처리
if("${info.CATEGORY_CD}" == '0000170' || "${info.CATEGORY_CD}" == '0000171'){
$(".DIRECT").show();
$(".SELECT").hide();
} else {
$(".DIRECT").hide();
$(".SELECT").show();
}
// 계약 결과가 완료된 경우 필드 비활성화
if("${info.CONTRACT_RESULT}" == "0000964"){
$("#category_cd,#area_cd,#target_project_no,#customer_objid,#product,#contract_result,#overhaul_order").prop("disabled","disabled");
$("#mechanical_type,#facility_qty,#target_project_no_direct").prop("readonly", true);
}
// 수정 모드에서는 저장 버튼 제거
if ("${actionType}" == "U") {
$("#btnSave").remove();
}
// 인증여부 초기값 설정
$("#certification_status").val("${info.CERTIFICATION_STATUS}");
// -----------------------------------------------------------
// 3-3. 입력 필드 포맷팅 및 플러그인 초기화
// -----------------------------------------------------------
// 숫자만 입력 가능한 필드에 콤마 자동 추가
$("input:text[numberOnly]").on("keyup", function() {
$(this).val(addComma($(this).val().replace(/[^0-9]/g, "")));
});
// Select2 플러그인 적용
$('.select2').select2();
// 날짜 선택기 초기화
_fnc_datepick();
// -----------------------------------------------------------
// 3-4. 첨부파일 드롭존 초기화 (기본 파일 첨부 영역)
// -----------------------------------------------------------
// 3개의 기본 파일 첨부 영역 초기화 (입수자료, 제출자료, 공유자료)
fnc_setFileDropZone2("contractMgmt01DropZone", "${objId}", "contractMgmt01", "contractMgmt01", "fileAreaDraw", false);
fnc_setFileDropZone2("contractMgmt02DropZone", "${objId}", "contractMgmt02", "contractMgmt02", "fileAreaDraw", false);
fnc_setFileDropZone2("contractMgmt03DropZone", "${objId}", "contractMgmt03", "contractMgmt03", "fileAreaDraw", false);
// 초기 파일 목록 로드
fileAreaDraw();
// -----------------------------------------------------------
// 3-5. 기존 버튼 이벤트 바인딩 (호환성 유지)
// -----------------------------------------------------------
// 기본 파일 업로드 버튼들
$("#btnUpload").click(function() {
var files = $("#file1")[0].files;
if (files.length > 0) {
fnc_fileMultiUpload(files, null, "${objId}", "contractMgmt01", "contractMgmt01", null, "fileAreaDraw");
$("#file1").val("");
} else {
Swal.fire("선택된 File이 없습니다.");
}
});
$("#btnUpload1").click(function() {
var files = $("#file2")[0].files;
if (files.length > 0) {
fnc_fileMultiUpload(files, null, "${objId}", "contractMgmt02", "contractMgmt02", null, "fileAreaDraw");
$("#file2").val("");
} else {
Swal.fire("선택된 File이 없습니다.");
}
});
$("#btnUpload2").click(function() {
var files = $("#file3")[0].files;
if (files.length > 0) {
fnc_fileMultiUpload(files, null, "${objId}", "contractMgmt03", "contractMgmt03", null, "fileAreaDraw");
$("#file3").val("");
} else {
Swal.fire("선택된 File이 없습니다.");
}
});
// 메인 버튼들
$("#btnSave").click(function() { fn_save(); });
$("#btnClose").click(function() { self.close(); });
// 파일 상세보기 버튼
$(".File").click(function() {
var popup_width = 800;
var popup_height = 335;
var objId = $(this).attr("data-OBJID");
var docType = $(this).attr("data-docType");
var docTypeName = $(this).attr("data-docTypeName");
var params = "?targetObjId=" + objId + "&docType=" + docType + "&docTypeName=" + docTypeName + "&callbackFnc=file_check";
var url = "/common/FileRegistPopup.do" + params;
fn_centerPopup(popup_width, popup_height, url);
});
// -----------------------------------------------------------
// 3-6. 제품군-제품코드 연동 기능
// -----------------------------------------------------------
// 제품군 선택 변경 시 제품코드 목록 업데이트
$("#product").change(function() {
var selectedProductCode = $(this).val();
console.log("제품군 변경됨:", selectedProductCode);
if (selectedProductCode && selectedProductCode !== "") {
// common.js의 fnc_getCodeListAppend 함수 사용하여 제품코드 목록 로드
fnc_getCodeListAppend(selectedProductCode, "product_code", "");
$("#product_code").select2(); // select2 재적용
console.log("제품코드 목록 로드 완료 for 제품군:", selectedProductCode);
} else {
// 제품군이 선택되지 않은 경우 제품코드 초기화
$("#product_code").empty();
$("#product_code").append('<option value="">선택</option>');
$("#product_code").select2();
console.log("제품코드 목록 초기화");
}
});
// 계약구분 변경 시 화면 전환
$("#category_cd").change(function(){
if($(this).val() == '0000170' || $(this).val() == '0000171'){
$(".DIRECT").show();
$(".SELECT").hide();
$("#target_project_no_direct").attr('required', 'required');
$("#overhaul_order").attr('required', 'required');
} else {
$(".DIRECT").hide();
$(".SELECT").show();
$("#target_project_no_direct").removeAttr('required');
$("#overhaul_order").removeAttr('required');
}
});
// -----------------------------------------------------------
// 3-7. 저장된 제품 정보 복원 (수정 모드용)
// -----------------------------------------------------------
var savedProductCode = "${info.PRODUCT}"; // 저장된 제품군 코드
var savedProductCodeValue = "${info.PRODUCT_CODE}"; // 저장된 제품코드 값
console.log("저장된 제품 정보 확인:");
console.log("- 제품군:", savedProductCode);
console.log("- 제품코드:", savedProductCodeValue);
// 저장된 제품 정보가 있는 경우 복원
if (savedProductCode && savedProductCode !== "" && savedProductCode !== "null") {
console.log("저장된 제품 정보 복원 시작");
// 제품군 값 설정
$("#product").val(savedProductCode);
if ($("#product").hasClass('select2-hidden-accessible')) {
$("#product").trigger('change.select2');
}
console.log("제품군 값 복원 완료:", savedProductCode);
// 제품코드 목록 로드 및 값 설정
if (savedProductCodeValue && savedProductCodeValue !== "" && savedProductCodeValue !== "null") {
fnc_getCodeListAppend(savedProductCode, "product_code", savedProductCodeValue);
console.log("제품코드 목록 로드 및 값 복원 완료:", savedProductCodeValue);
} else {
fnc_getCodeListAppend(savedProductCode, "product_code", "");
console.log("제품코드 목록만 로드 완료 (선택값 없음)");
}
$("#product_code").select2(); // select2 재적용
console.log("제품 정보 복원 작업 완료");
} else {
console.log("저장된 제품 정보 없음 - 신규 등록 모드");
}
// -----------------------------------------------------------
// 3-8. 견적이력관리 기능 초기화
// -----------------------------------------------------------
// 전체 선택/해제 체크박스 이벤트
$("#allQuoteCheck").change(function() {
$("input[name='quote_check']").prop('checked', $(this).prop('checked'));
});
// 견적 행 추가/삭제 버튼 이벤트
$("#btnAddQuoteRow").click(function() {
addNewQuoteRow();
});
$("#btnDeleteQuoteRow").click(function() {
deleteSelectedQuoteRows();
});
// -----------------------------------------------------------
// 3-9. 기존 견적 데이터 초기화 (JSTL로 렌더링된 행들 처리)
// -----------------------------------------------------------
// 페이지 로드 후 기존 견적 데이터의 이벤트 바인딩 및 파일 로드
setTimeout(function() {
initializeExistingQuoteRows();
}, 500); // 다른 초기화가 완료된 후 실행
});
// ===================================================================
// 4. 기본 비즈니스 로직 함수들
// ===================================================================
// -----------------------------------------------------------
// 4-1. 수주가 자동계산 함수
// -----------------------------------------------------------
function calculateOrderAmount() {
var quoteAmount1st = parseFloat($("#quote_amount_1st").val()) || 0;
var quoteAmount2nd = parseFloat($("#quote_amount_2nd").val()) || 0;
var quoteAmount3rd = parseFloat($("#quote_amount_3rd").val()) || 0;
var facilityQty = parseFloat($("#facility_qty").val()) || 0;
// 견적금액 중 최신값(3차 > 2차 > 1차) 선택
var latestQuoteAmount = 0;
if (quoteAmount3rd > 0) {
latestQuoteAmount = quoteAmount3rd;
} else if (quoteAmount2nd > 0) {
latestQuoteAmount = quoteAmount2nd;
} else if (quoteAmount1st > 0) {
latestQuoteAmount = quoteAmount1st;
}
// 수주가 = 최신 견적금액 * 설비대수
var orderAmount = latestQuoteAmount * facilityQty;
if (orderAmount > 0) {
$("#contract_price").val(addComma(orderAmount.toFixed(0)));
} else {
$("#contract_price").val("");
}
}
// -----------------------------------------------------------
// 4-2. 날짜 선택기 초기화 함수
// -----------------------------------------------------------
function _fnc_datepick() {
var $dateinput = $("input.date_icon");
for (var i = 0; i < $dateinput.length; i++) {
$dateinput.eq(i).attr("size", "10");
$dateinput.eq(i).datepicker({
changeMonth : true,
changeYear : true
});
}
}
// -----------------------------------------------------------
// 4-3. 저장 함수 (메인 비즈니스 로직)
// -----------------------------------------------------------
function fn_save() {
try {
if (fnc_valitate("form1")) {
var message = "수정";
if ("${actionType}" == 'regist') {
message = "등록";
}
if (confirm(message + "하시겠습니까?")) {
// 견적 데이터 수집
var quoteData = collectQuoteData();
// 기본 폼 데이터와 견적 데이터를 함께 전송
var formData = $("#form1").serialize();
if (quoteData.length > 0) {
formData += "&quoteData=" + encodeURIComponent(JSON.stringify(quoteData));
}
$.ajax({
url : "/contractMgmt/saveContractMgmtInfo.do",
type : "POST",
data : formData,
dataType : "json",
success : function(data) {
try {
alert(data.msg || "저장이 완료되었습니다.");
if (opener && typeof opener.fn_search === 'function') {
opener.fn_search();
}
self.close();
} catch(e) {
console.error('저장 후 처리 실패:', e);
alert("저장은 완료되었지만 화면 갱신 중 오류가 발생했습니다.");
}
},
error : function(jqxhr, status, error) {
console.error("저장 실패:", error);
alert("저장 중 오류가 발생했습니다. 다시 시도해주세요.");
}
});
}
}
} catch(e) {
console.error('저장 함수 실행 실패:', e);
alert("저장 중 예기치 않은 오류가 발생했습니다.");
}
}
// ===================================================================
// 5. 파일 관리 함수들 (기본 첨부파일용)
// ===================================================================
// -----------------------------------------------------------
// 5-1. 개선된 드래그 앤 드롭 기능
// -----------------------------------------------------------
function fnc_setFileDropZone2(divId, targetObjId, docType, docTypeName, callbackFnc, onlyOne, preProcessor, url, okExt) {
try {
var obj = $("#" + divId);
if (obj.length === 0) {
console.warn('드롭존 요소를 찾을 수 없습니다:', divId);
return;
}
// 드래그 앤 드롭 이벤트 처리
obj.on('dragenter', function(e) {
e.stopPropagation();
e.preventDefault();
$(this).addClass('dragover');
});
obj.on('dragleave', function(e) {
e.stopPropagation();
e.preventDefault();
$(this).removeClass('dragover');
});
obj.on('dragover', function(e) {
e.stopPropagation();
e.preventDefault();
});
obj.on('drop', function(e) {
try {
e.preventDefault();
$(this).removeClass('dragover');
var files = e.originalEvent.dataTransfer.files;
if (files.length < 1) return;
if (onlyOne && files.length > 1) {
if (typeof Swal !== 'undefined') {
Swal.fire("1개의 파일만 등록 가능합니다.");
} else {
alert("1개의 파일만 등록 가능합니다.");
}
return;
}
// 드롭된 파일 즉시 업로드 처리
processFileUpload(files, targetObjId, docType, docTypeName, callbackFnc, url, okExt);
} catch(e) {
console.error('파일 드롭 처리 실패:', e);
}
});
// 클릭 이벤트 처리 - 자동 업로드 기능 추가
obj.on('click', function() {
try {
// 숨겨진 파일 입력 요소 생성
var input = $('<input type="file" multiple style="display:none;" accept="*/*">');
// 파일 선택 시 자동 업로드 처리
input.on('change', function() {
try {
var files = this.files;
if (files.length > 0) {
// 선택된 파일을 즉시 업로드 처리
processFileUpload(files, targetObjId, docType, docTypeName, callbackFnc, url, okExt);
}
// 사용 후 요소 제거
$(this).remove();
} catch(e) {
console.error('파일 선택 처리 실패:', e);
$(this).remove();
}
});
// DOM에 추가하고 클릭 트리거
$('body').append(input);
input.click();
} catch(e) {
console.error('파일 선택 대화상자 열기 실패:', e);
}
});
} catch(e) {
console.error('드롭존 초기화 실패:', e);
}
}
// -----------------------------------------------------------
// 5-2. 파일 업로드 처리 통합 함수
// -----------------------------------------------------------
function processFileUpload(files, targetObjId, docType, docTypeName, callbackFnc, url, okExt) {
try {
// 기본 URL 설정
if (!url) url = "/common/fileUploadProc.do";
// 파일 검증 및 업로드 실행
fnc_fileMultiUpload1(files, null, targetObjId, docType, docTypeName, url, callbackFnc, okExt);
} catch(e) {
console.error('파일 업로드 처리 실패:', e);
if (typeof Swal !== 'undefined') {
Swal.fire("파일 처리 중 오류가 발생했습니다.");
} else {
alert("파일 처리 중 오류가 발생했습니다.");
}
}
}
// -----------------------------------------------------------
// 5-3. 파일 검증 및 업로드 실행 함수
// -----------------------------------------------------------
function fnc_fileMultiUpload1(files, obj, targetObjId, docType, docTypeName, uploadUrl, callbackFnc, okExt) {
try {
// 파일 검증 단계
if (!validateFiles(files, okExt)) {
return; // 검증 실패 시 업로드 중단
}
// 업로드 확인 메시지
if (confirm(files.length + "개의 파일을 업로드 하시겠습니까?")) {
executeFileUpload(files, targetObjId, docType, docTypeName, uploadUrl, callbackFnc);
}
} catch(e) {
console.error('파일 업로드 함수 실행 실패:', e);
if (typeof Swal !== 'undefined') {
Swal.fire("파일 업로드 중 예기치 않은 오류가 발생했습니다.");
} else {
alert("파일 업로드 중 예기치 않은 오류가 발생했습니다.");
}
}
}
// -----------------------------------------------------------
// 5-4. 파일 검증 함수
// -----------------------------------------------------------
function validateFiles(files, okExt) {
try {
if (!files || files.length === 0) {
return false;
}
// 허용된 확장자 검증
if (okExt != null && okExt !== "") {
for (var i = 0; i < files.length; i++) {
var fileName = files[i].name;
var ext = fileName.substring(fileName.lastIndexOf(".") + 1).toUpperCase() + ",";
if ((okExt.toUpperCase() + ",").indexOf(ext) < 0) {
if (typeof Swal !== 'undefined') {
Swal.fire("허용되지 않은 확장자입니다.\n업로드 가능 확장자 : " + okExt);
} else {
alert("허용되지 않은 확장자입니다.\n업로드 가능 확장자 : " + okExt);
}
return false;
}
}
} else {
// 위험한 확장자 차단
var dangerousExt = "JSP,BAT,EXE,PHP,JS,SQL";
for (var i = 0; i < files.length; i++) {
var fileName = files[i].name;
var ext = fileName.substring(fileName.lastIndexOf(".") + 1).toUpperCase();
if (dangerousExt.toUpperCase().indexOf(ext) >= 0) {
if (typeof Swal !== 'undefined') {
Swal.fire("보안상 허용되지 않은 확장자입니다: " + ext);
} else {
alert("보안상 허용되지 않은 확장자입니다: " + ext);
}
return false;
}
}
}
// 파일명 특수문자 검증
for (var i = 0; i < files.length; i++) {
var fileName = files[i].name;
var pattern = /[#%&*+[\]]/;
if (pattern.test(fileName)) {
if (typeof Swal !== 'undefined') {
Swal.fire("파일명에 허용되지 않은 특수문자가 포함되어 있습니다(#,%,&,*,+,[,]).");
} else {
alert("파일명에 허용되지 않은 특수문자가 포함되어 있습니다(#,%,&,*,+,[,]).");
}
return false;
}
}
return true; // 모든 검증 통과
} catch(e) {
console.error('파일 검증 실패:', e);
return false;
}
}
// -----------------------------------------------------------
// 5-5. 실제 파일 업로드 실행 함수
// -----------------------------------------------------------
function executeFileUpload(files, targetObjId, docType, docTypeName, uploadUrl, callbackFnc) {
try {
// 업로드 URL 기본값 설정
uploadUrl = fnc_checkNull(uploadUrl);
if (uploadUrl == "") uploadUrl = "/common/fileUploadProc.do";
// FormData 생성 및 파일 추가
var data = new FormData();
for (var i = 0; i < files.length; i++) {
data.append('file', files[i]);
}
// 업로드 메타데이터 추가
data.append("targetObjId", targetObjId);
data.append("docType", docType);
data.append("docTypeName", docTypeName);
// Ajax 업로드 실행
$.ajax({
url : uploadUrl,
method : 'post',
data : data,
dataType : 'text',
processData : false,
contentType : false,
beforeSend : function() {
console.log("파일 업로드 시작...");
},
success : function(res) {
try {
// 업로드 성공 시 콜백 함수 실행
if (fnc_checkNull(callbackFnc) != "") {
if (typeof window[callbackFnc] === 'function') {
window[callbackFnc]();
} else {
eval(callbackFnc + "();");
}
}
console.log("파일 업로드 완료");
} catch(e) {
console.error('업로드 후 콜백 실행 실패:', e);
}
},
error : function(jqxhr, status, error) {
console.error("업로드 실패:", error);
if (typeof Swal !== 'undefined') {
Swal.fire("업로드 중 오류가 발생했습니다: " + error);
} else {
alert("업로드 중 오류가 발생했습니다: " + error);
}
}
});
} catch(e) {
console.error('파일 업로드 함수 실행 실패:', e);
if (typeof Swal !== 'undefined') {
Swal.fire("파일 업로드 중 예기치 않은 오류가 발생했습니다.");
} else {
alert("파일 업로드 중 예기치 않은 오류가 발생했습니다.");
}
}
}
// -----------------------------------------------------------
// 5-6. 파일 영역 다시 그리기 및 콜백 함수들
// -----------------------------------------------------------
// 기본 파일 영역 다시 그리기
function fileAreaDraw() {
fn_fileCallback("contractMgmt01", "contractMgmt01");
fn_fileCallback("contractMgmt02", "contractMgmt02");
fn_fileCallback("contractMgmt03", "contractMgmt03");
}
// 파일 목록 콜백 함수 - 간단한 UI에 최적화
function fn_fileCallback(areaId, fileType) {
try {
$.ajax({
url : "/common/getFileList.do",
type : "POST",
data : {
"targetObjId" : "${objId}",
"docType" : fileType
},
dataType : "json",
async : false,
success : function(data) {
try {
var fileArea = $("#" + areaId + "FileArea");
if (fileArea.length === 0) {
console.warn('파일 영역을 찾을 수 없습니다:', areaId + "FileArea");
return;
}
fileArea.empty();
if (data && data.length > 0) {
var appendText = "";
$.each(data, function(i) {
appendText += "<tr>";
appendText += " <td>";
// 파일명 표시
var displayName = data[i].REAL_FILE_NAME || '파일명 없음';
if (displayName.length > 25) {
displayName = displayName.substring(0, 22) + "...";
}
appendText += " <a href='javascript:fnc_downloadFile(\"" + data[i].OBJID + "\")' class='file_link' title='" + (data[i].REAL_FILE_NAME || '') + "'>";
appendText += " " + displayName;
appendText += " </a>";
// 권한에 따른 삭제 버튼 표시
<c:if test="${param.actionType eq 'regist' or info.WRITER eq connectUserId or 'plm_admin' eq connectUserId}">
appendText += " <a href='javascript:fileDelete(\"" + data[i].OBJID + "\",\"" + areaId + "\")'>";
appendText += " <div class='delete_btn' title='파일 삭제'></div>";
appendText += " </a>";
</c:if>
appendText += " </td>";
appendText += "</tr>";
});
fileArea.append(appendText);
} else {
// 빈 목록 메시지
fileArea.append("<tr><td class='empty_file_message'>등록된 파일이 없습니다</td></tr>");
}
} catch(e) {
console.error('파일 목록 표시 실패:', e);
}
},
error : function(jqxhr, status, error) {
console.error("파일 목록 로드 실패:", error);
}
});
} catch(e) {
console.error('파일 콜백 함수 실행 실패:', e);
}
}
// 파일 삭제 함수
function fileDelete(fileObjId, areaId) {
try {
if (!fileObjId || !areaId) {
console.error('파일 삭제에 필요한 정보가 없습니다.');
return;
}
if (confirm("파일을 삭제하시겠습니까?")) {
$.ajax({
url : "/common/deleteFileInfo.do",
type : "POST",
data : { "objId" : fileObjId },
dataType : "json",
async : true,
success : function(data) {
try {
fileAreaDraw(); // 삭제 후 목록 새로고침
} catch(e) {
console.error('파일 삭제 후 목록 새로고침 실패:', e);
}
},
error : function(jqxhr, status, error) {
console.error("파일 삭제 실패:", error);
if (typeof Swal !== 'undefined') {
Swal.fire("파일 삭제 중 오류가 발생했습니다.");
} else {
alert("파일 삭제 중 오류가 발생했습니다.");
}
}
});
}
} catch(e) {
console.error('파일 삭제 함수 실행 실패:', e);
}
}
// 파일 체크 함수
function file_check(doctype, filesize) {
try {
var element = $("#" + doctype);
if (element.length === 0) {
console.warn('파일 체크 대상 요소를 찾을 수 없습니다:', doctype);
return;
}
if (filesize > 0) {
element.removeClass("file_icon file_empty_icon");
element.addClass("file_icon");
} else {
element.removeClass("file_icon file_empty_icon");
element.addClass("file_empty_icon");
}
} catch(e) {
console.error('파일 체크 함수 실행 실패:', e);
}
}
// ===================================================================
// 6. 견적이력관리 전용 함수들 - JSTL 적용 버전 (일괄삭제 기능 포함)
// ===================================================================
// -----------------------------------------------------------
// 6-1. 기존 견적 행들의 초기화 (JSTL로 렌더링된 행들 처리)
// -----------------------------------------------------------
function initializeExistingQuoteRows() {
console.log("기존 견적 행 초기화 시작...");
// JSTL로 렌더링된 모든 견적 행에 대해 초기화 작업 수행
$('#quoteTableBody tr.quote-row').each(function() {
var row = $(this);
var objId = row.data('objid');
if (objId) {
console.log("견적 행 초기화 - objId:", objId);
// Select2 적용
try {
row.find('.select2').select2();
} catch(e) {
console.warn('Select2 적용 실패:', e);
}
// 숫자 전용 입력 필드 설정
row.find('input[numberOnly]').on("keyup change", function() {
$(this).val(addComma($(this).val().replace(/[^0-9]/g, "")));
});
// 첨부파일 드롭존 초기화
try {
initQuoteFileDropZone(objId);
} catch(e) {
console.error('드롭존 초기화 실패:', e);
}
// 개별 체크박스 이벤트
row.find('input[name="quote_check"]').change(function() {
updateAllQuoteCheckStatus();
});
// 기존 파일 목록 로드
setTimeout(function() {
fn_quoteFileCallback("quoteFileList_" + objId, "QUOTE_DOCUMENT", objId);
}, 200);
}
});
console.log("기존 견적 행 초기화 완료");
}
// -----------------------------------------------------------
// 6-2. 새로운 견적 행 추가
// -----------------------------------------------------------
function addNewQuoteRow() {
quoteRowCounter++;
var newObjId = fnc_createObjId();
var today = new Date().toISOString().split('T')[0];
console.log("새로운 견적 행 추가 - objId:", newObjId);
// "데이터 없음" 행 제거
$('#noQuoteDataRow').remove();
var newRowHTML =
'<tr class="quote-row" data-objid="' + newObjId + '" data-is-new="true">' +
' <td style="text-align:center;">' +
' <input type="checkbox" name="quote_check" class="quote_row_checkbox">' +
' </td>' +
' <td>' +
' <input type="text" name="quote_sequence" class="quote_input" placeholder="차수" readonly value="' + quoteRowCounter + '">' +
' </td>' +
' <td>' +
' <select name="currency_type" class="quote_select select2" required>' +
currencyTypeOptions +
' </select>' +
' </td>' +
' <td>' +
' <input type="text" name="quote_amount" class="quote_input" placeholder="견적금액" numberOnly>' +
' </td>' +
' <td>' +
' <input type="date" name="quote_submit_date" class="quote_input" value="' + today + '">' +
' </td>' +
' <td>' +
' <div class="quote_file_container">' +
' <div id="quoteDropZone_' + newObjId + '" class="quote_dropzone" data-objid="' + newObjId + '">' +
' 견적서 첨부 (클릭 또는 드래그앤드롭)' +
' </div>' +
' <div id="quoteFileList_' + newObjId + '" class="quote_file_list" style="display:none;">' +
' </div>' +
' </div>' +
' <input type="hidden" name="quote_objid" value="' + newObjId + '">' +
' </td>' +
'</tr>';
// 테이블에 새 행 추가
$('#quoteTableBody').append(newRowHTML);
// 새로 추가된 행에 이벤트 바인딩
var newRow = $('#quoteTableBody tr:last');
// Select2 적용
try {
newRow.find('.select2').select2();
} catch(e) {
console.warn('Select2 적용 실패:', e);
}
// 숫자 전용 입력 필드 설정
newRow.find('input[numberOnly]').on("keyup change", function() {
$(this).val(addComma($(this).val().replace(/[^0-9]/g, "")));
});
// 첨부파일 드롭존 초기화 - 중요: 새로 추가된 행에서도 작동하도록
try {
initQuoteFileDropZone(newObjId);
} catch(e) {
console.error('드롭존 초기화 실패:', e);
}
// 개별 체크박스 이벤트
newRow.find('input[name="quote_check"]').change(function() {
updateAllQuoteCheckStatus();
});
console.log("견적 행 추가 완료:", newObjId);
}
// -----------------------------------------------------------
// 6-3. 선택된 견적 행 일괄 삭제 (배열 처리 방식으로 수정)
// -----------------------------------------------------------
function deleteSelectedQuoteRows() {
var checkedRows = $('input[name="quote_check"]:checked');
if (checkedRows.length === 0) {
Swal.fire({
icon: 'warning',
title: '선택 확인',
text: '삭제할 견적 정보를 선택해주세요.'
});
return;
}
// 삭제 확인 대화상자
Swal.fire({
title: '삭제 확인',
text: checkedRows.length + '개의 견적 정보를 삭제하시겠습니까?',
icon: 'question',
showCancelButton: true,
confirmButtonText: '삭제',
cancelButtonText: '취소',
confirmButtonColor: '#d33'
}).then((result) => {
if (result.isConfirmed) {
// 삭제 대상 objId 배열 수집
var targetObjIdList = [];
var newRowsToDelete = []; // 신규 추가된 행들 (DB에 없는 행)
checkedRows.each(function() {
var row = $(this).closest('tr');
var objId = row.data('objid');
var isNew = row.data('is-new');
if (objId) {
if (isNew) {
// 신규 추가된 행 (DB에 저장되지 않은 행)
newRowsToDelete.push(row);
} else {
// 기존 데이터 (DB에서 삭제 필요)
targetObjIdList.push(objId);
}
}
});
console.log("삭제 대상 - DB 저장된 견적:", targetObjIdList);
console.log("삭제 대상 - 신규 견적:", newRowsToDelete.length + "개");
// 1. 먼저 신규 추가된 행들을 화면에서 제거
$(newRowsToDelete).each(function() {
$(this).remove();
});
// 2. DB에 저장된 견적이 있는 경우 서버에 일괄 삭제 요청
if (targetObjIdList.length > 0) {
deleteQuotesFromServer(targetObjIdList, checkedRows);
} else {
// DB 삭제할 항목이 없는 경우 화면 갱신만 처리
updateQuoteTableAfterDelete();
showDeleteSuccessMessage(checkedRows.length);
}
}
});
}
// -----------------------------------------------------------
// 6-4. 서버에서 견적 데이터 일괄 삭제 (새로 추가된 함수)
// -----------------------------------------------------------
function deleteQuotesFromServer(targetObjIdList, originalCheckedRows) {
console.log("서버 일괄 삭제 요청:", targetObjIdList);
$.ajax({
url: "/contractMgmt/deleteQuoteInfoMultiple.do", // 새로운 일괄 삭제 URL
type: "POST",
data: {
"checkArr": targetObjIdList.join(",") // 배열을 콤마로 구분된 문자열로 변환
},
dataType: "json",
async: false,
success: function(data) {
try {
console.log("견적 일괄 삭제 응답:", data);
if (data.result) {
// 삭제 성공 시 화면에서 해당 행들 제거
originalCheckedRows.each(function() {
var row = $(this).closest('tr');
var objId = row.data('objid');
var isNew = row.data('is-new');
// DB에 저장된 항목만 제거 (신규 항목은 이미 제거됨)
if (objId && !isNew && targetObjIdList.includes(objId)) {
row.remove();
}
});
updateQuoteTableAfterDelete();
showDeleteSuccessMessage(originalCheckedRows.length);
} else {
Swal.fire({
icon: 'error',
title: '삭제 실패',
text: data.msg || '견적 삭제 중 오류가 발생했습니다.'
});
}
} catch(e) {
console.error('삭제 응답 처리 실패:', e);
Swal.fire({
icon: 'error',
title: '처리 오류',
text: '삭제 응답 처리 중 오류가 발생했습니다.'
});
}
},
error: function(jqxhr, status, error) {
console.error("견적 일괄 삭제 실패:", error);
Swal.fire({
icon: 'error',
title: '서버 오류',
text: '서버 통신 중 오류가 발생했습니다: ' + error
});
}
});
}
// -----------------------------------------------------------
// 6-5. 삭제 후 테이블 상태 업데이트
// -----------------------------------------------------------
function updateQuoteTableAfterDelete() {
// 모든 행이 삭제된 경우 "데이터 없음" 메시지 표시
if ($('#quoteTableBody tr.quote-row').length === 0) {
$('#quoteTableBody').html(
'<tr id="noQuoteDataRow">' +
'<td colspan="6" style="text-align:center; color:#999; font-style:italic; padding:20px;">' +
'등록된 견적 정보가 없습니다' +
'</td>' +
'</tr>'
);
}
// 전체 선택 체크박스 상태 업데이트
updateAllQuoteCheckStatus();
}
// -----------------------------------------------------------
// 6-6. 삭제 성공 메시지 표시
// -----------------------------------------------------------
function showDeleteSuccessMessage(deletedCount) {
Swal.fire({
icon: 'success',
title: '삭제 완료',
text: deletedCount + '개의 견적 정보가 성공적으로 삭제되었습니다.',
timer: 1500,
showConfirmButton: false
});
}
// -----------------------------------------------------------
// 6-7. 견적서 첨부파일 드롭존 초기화
// -----------------------------------------------------------
function initQuoteFileDropZone(objId) {
var dropZone = $("#quoteDropZone_" + objId);
if (dropZone.length === 0) {
console.warn('드롭존을 찾을 수 없습니다:', "quoteDropZone_" + objId);
return;
}
console.log("견적서 드롭존 초기화 시작 - objId:", objId);
// 기존 이벤트 제거 (중복 바인딩 방지)
dropZone.off('dragenter dragleave dragover drop click');
// 드래그 앤 드롭 이벤트 처리
dropZone.on('dragenter', function(e) {
e.stopPropagation();
e.preventDefault();
$(this).addClass('dragover');
});
dropZone.on('dragleave', function(e) {
e.stopPropagation();
e.preventDefault();
$(this).removeClass('dragover');
});
dropZone.on('dragover', function(e) {
e.stopPropagation();
e.preventDefault();
});
dropZone.on('drop', function(e) {
try {
e.preventDefault();
$(this).removeClass('dragover');
var files = e.originalEvent.dataTransfer.files;
if (files.length > 0) {
uploadQuoteFiles(files, objId);
}
} catch(e) {
console.error('견적서 파일 드롭 처리 실패:', e);
}
});
// 클릭 이벤트 처리
dropZone.on('click', function() {
try {
var input = $('<input type="file" multiple style="display:none;" accept="*/*">');
input.on('change', function() {
var files = this.files;
if (files.length > 0) {
uploadQuoteFiles(files, objId);
}
$(this).remove();
});
$('body').append(input);
input.click();
} catch(e) {
console.error('파일 선택 대화상자 열기 실패:', e);
}
});
console.log("견적서 드롭존 초기화 완료 - objId:", objId);
}
// -----------------------------------------------------------
// 6-8. 견적서 파일 업로드 함수
// -----------------------------------------------------------
function uploadQuoteFiles(files, objId) {
if (!files || files.length === 0) return;
console.log("견적서 파일 업로드 시작 - objId:", objId, "파일 수:", files.length);
// 기본 파일 검증
for (var i = 0; i < files.length; i++) {
var fileName = files[i].name;
var ext = fileName.substring(fileName.lastIndexOf(".") + 1).toUpperCase();
var dangerousExt = ["JSP", "BAT", "EXE", "PHP", "JS", "SQL"];
if (dangerousExt.indexOf(ext) >= 0) {
if (typeof Swal !== 'undefined') {
Swal.fire("보안상 허용되지 않은 확장자입니다: " + ext);
} else {
alert("보안상 허용되지 않은 확장자입니다: " + ext);
}
return;
}
}
// 업로드 처리
try {
var data = new FormData();
for (var i = 0; i < files.length; i++) {
data.append('file', files[i]);
}
data.append("targetObjId", objId);
data.append("docType", "QUOTE_DOCUMENT");
data.append("docTypeName", "견적서");
$.ajax({
url: "/common/fileUploadProc.do",
method: 'post',
data: data,
dataType: 'text',
processData: false,
contentType: false,
success: function(res) {
try {
console.log("견적서 파일 업로드 완료 - objId:", objId);
fn_quoteFileCallback("quoteFileList_" + objId, "QUOTE_DOCUMENT", objId);
} catch(e) {
console.error('업로드 후 콜백 실행 실패:', e);
}
},
error: function(jqxhr, status, error) {
console.error("견적서 파일 업로드 실패:", error);
if (typeof Swal !== 'undefined') {
Swal.fire("업로드 중 오류가 발생했습니다: " + error);
} else {
alert("업로드 중 오류가 발생했습니다: " + error);
}
}
});
} catch(e) {
console.error('견적서 파일 업로드 실패:', e);
alert('파일 업로드 중 오류가 발생했습니다.');
}
}
// -----------------------------------------------------------
// 6-9. 견적서 파일 목록 콜백 함수
// -----------------------------------------------------------
function fn_quoteFileCallback(areaId, fileType, targetObjId) {
console.log("견적서 파일 목록 로드 시작 - areaId:", areaId, "targetObjId:", targetObjId);
$.ajax({
url: "/common/getFileList.do",
type: "POST",
data: {
"targetObjId": targetObjId,
"docType": fileType
},
dataType: "json",
success: function(data) {
try {
var fileArea = $("#" + areaId);
if (fileArea.length === 0) {
console.warn('파일 영역을 찾을 수 없습니다:', areaId);
return;
}
fileArea.empty();
console.log("견적서 파일 목록 응답:", data);
if (data && data.length > 0) {
fileArea.show();
$.each(data, function(i, file) {
var fileItem = $('<div class="quote_file_item"></div>');
var fileName = file.REAL_FILE_NAME;
if (fileName.length > 20) {
fileName = fileName.substring(0, 17) + "...";
}
var fileLink = $('<a href="javascript:fnc_downloadFile(\'' + file.OBJID + '\')" class="quote_file_link" title="' + file.REAL_FILE_NAME + '">' + fileName + '</a>');
var deleteBtn = $('<div class="quote_delete_btn" onclick="deleteQuoteFile(\'' + file.OBJID + '\', \'' + targetObjId + '\')" title="파일 삭제"></div>');
fileItem.append(fileLink);
fileItem.append(deleteBtn);
fileArea.append(fileItem);
});
console.log("견적서 파일 목록 표시 완료 - " + data.length + "개 파일");
} else {
fileArea.hide();
console.log("견적서 파일 목록 없음");
}
} catch(e) {
console.error('견적서 파일 목록 처리 실패:', e);
}
},
error: function(jqxhr, status, error) {
console.error("견적서 파일 목록 로드 실패:", error);
}
});
}
// -----------------------------------------------------------
// 6-10. 견적서 파일 삭제
// -----------------------------------------------------------
function deleteQuoteFile(fileObjId, parentObjId) {
if (confirm("파일을 삭제하시겠습니까?")) {
$.ajax({
url: "/common/deleteFileInfo.do",
type: "POST",
data: { "objId": fileObjId },
dataType: "json",
success: function(data) {
fn_quoteFileCallback("quoteFileList_" + parentObjId, "QUOTE_DOCUMENT", parentObjId);
},
error: function(jqxhr, status, error) {
console.error("파일 삭제 실패:", error);
}
});
}
}
// -----------------------------------------------------------
// 6-11. 기타 견적 관리 함수들
// -----------------------------------------------------------
// 기존 개별 삭제 함수 (유지 - 필요시 사용)
function deleteQuoteFromServer(objId) {
if (!objId) {
console.warn('삭제할 견적 objId가 없습니다.');
return;
}
console.log("개별 견적 데이터 삭제 요청:", objId);
$.ajax({
url: "/contractMgmt/deleteQuoteInfo.do", // 기존 개별 삭제 URL
type: "POST",
data: { "objId": objId },
dataType: "json",
success: function(data) {
if (data.result) {
console.log("견적 정보 삭제 완료:", objId);
} else {
console.error("견적 정보 삭제 실패:", data.msg);
}
},
error: function(jqxhr, status, error) {
console.error("견적 정보 삭제 실패:", error);
}
});
}
// 전체 선택 체크박스 상태 업데이트
function updateAllQuoteCheckStatus() {
try {
var totalCheckboxes = $('input[name="quote_check"]').length;
var checkedCheckboxes = $('input[name="quote_check"]:checked').length;
if (totalCheckboxes === 0) {
$("#allQuoteCheck").prop('checked', false);
} else if (checkedCheckboxes === totalCheckboxes) {
$("#allQuoteCheck").prop('checked', true);
} else {
$("#allQuoteCheck").prop('checked', false);
}
} catch(e) {
console.error('체크박스 상태 업데이트 실패:', e);
}
}
// 견적 데이터 수집 (저장 시 사용)
function collectQuoteData() {
var quoteData = [];
try {
$('#quoteTableBody tr.quote-row').each(function() {
var row = $(this);
var objId = row.data('objid');
var isNew = row.data('is-new');
if (!objId) {
console.warn('견적 행에 objId가 없습니다.');
return true; // continue
}
var sequence = row.find('input[name="quote_sequence"]').val() || '';
var currencyType = row.find('select[name="currency_type"]').val() || '';
var amount = row.find('input[name="quote_amount"]').val() || '';
var submitDate = row.find('input[name="quote_submit_date"]').val() || '';
var originalObjId = row.find('input[name="quote_original_objid"]').val() || '';
// 금액에서 콤마 제거
if (amount) {
amount = amount.replace(/,/g, '');
}
var data = {
objId: objId,
originalObjId: originalObjId,
sequence: sequence,
currencyType: currencyType,
amount: amount,
submitDate: submitDate,
isNew: isNew
};
quoteData.push(data);
});
} catch(e) {
console.error('견적 데이터 수집 실패:', e);
}
console.log("수집된 견적 데이터:", quoteData);
return quoteData;
}
// ===================================================================
// 7. 기타 유틸리티 함수들
// ===================================================================
// 숫자 전용 입력 처리
function fnc_numberOnly(obj) {
$("#" + obj.attr("id")).val(addComma(obj.val().replace(/[^0-9]/g, "")));
}
// 로딩 관련 함수들
function _startLoading1(loadingMsg) {
$(".loader_back").hide();
$(".loader").show();
}
function _endLoading1() {
$(".loader").hide();
$(".loader_back").show();
}
// 파일 콜백 함수 (레거시)
function fnc_fileMultiUpload1_Callback(files) {
for (var i = 0; i < files.length; i++)
console.log(files[i].file_nm + " - " + files[i].file_size);
}
</script>
</head>
<body>
<form name="form1" id="form1" action="" method="post">
<input type="hidden" name="objId" id="objId" value="${objId}">
<input type="hidden" name="option" id="option">
<input type="hidden" name="actionType" id="actionType" value="${actionType}">
<section class="business_popup_min_width">
<div id="EntirePopupFormWrap">
<div class="form_popup_title">
<span>사양상세</span>
</div>
<table class="">
<tr>
<td>
<table class="pmsPopuptable">
<colgroup>
<col width="16.66%" />
<col width="16.66%" />
<col width="16.66%" />
<col width="16.66%" />
<col width="16.66%" />
<col width="16.66%" />
</colgroup>
<tr>
<td class="input_title"><label for="">재질</label></td>
<td class="input_title"><label for="">압력(BAR)</label></td>
<td class="input_title"><label for="">온도(℃)</label></td>
<td class="input_title"><label for="">용량(LITER)</label></td>
<td class="input_title"><label for="">I.D(mm)</label></td>
<td class="input_title"><label for="">I.L(mm)</label></td>
</tr>
<tr>
<td>
<input type="text" name="material" id="material" reqTitle="재질" value="${info.MATERIAL}" placeholder="직접 입력" />
</td>
<td>
<input type="number" step="0.01" name="pressure_bar" id="pressure_bar" reqTitle="압력(BAR)" value="${info.PRESSURE_BAR}" placeholder="숫자 입력" />
</td>
<td>
<input type="number" step="0.01" name="temperature_celsius" id="temperature_celsius" reqTitle="온도(℃)" value="${info.TEMPERATURE_CELSIUS}" placeholder="숫자 입력" />
</td>
<td>
<input type="number" step="0.01" name="capacity_liter" id="capacity_liter" reqTitle="용량(LITER)" value="${info.CAPACITY_LITER}" placeholder="숫자 입력" />
</td>
<td>
<input type="text" name="idmm" id="idmm" reqTitle="idmm" value="${info.IDMM}" placeholder="입력" />
</td>
<td>
<input type="text" name="ilmm" id="ilmm" reqTitle="ilmm" value="${info.ILMM}" placeholder="입력" />
</td>
</tr>
<tr>
<td class="input_title"><label for="">Closure type</label></td>
<td class="input_title"><label for="">기타(소모품)</label></td>
<td class="input_title"><label for="">전압</label></td>
<td class="input_title"><label for="">인증여부</label></td>
</tr>
<tr>
<td>
<input type="text" name="closure_type" id="closure_type" reqTitle="Closure Type" value="${info.CLOSURE_TYPE}" placeholder="직접 입력" />
</td>
<td>
<input type="text" name="consumables_etc" id="consumables_etc" reqTitle="기타(소모품)" value="${info.CONSUMABLES_ETC}" placeholder="직접 입력" />
</td>
<td>
<input type="text" name="voltage" id="voltage" reqTitle="전압" value="${info.VOLTAGE}" placeholder="직접 입력" />
</td>
<td>
<select name="certification_status" id="certification_status" required reqTitle="인증여부" type="select" class="select2">
<option value="">선택</option>
<option value="Y">필요</option>
<option value="N">불필요</option>
</select>
</td>
</tr>
</table>
</td>
</tr>
</table>
</div>
<div class="btn_wrap">
<div class="plm_btn_wrap_center">
<%-- <c:choose>
<c:when test="${actionType eq 'regist'}">
<input type="button" value="저장" id="btnSave" class="plm_btns">
</c:when>
<c:otherwise>
<input type="button" value="수정" id="btnSave" class="plm_btns">
</c:otherwise>
</c:choose> --%>
<input type="button" value="닫기" id="btnClose" class="plm_btns">
</div>
</div>
</section>
</form>
</body>
</html>