- URL 하드코딩 → .env APP_URL 기반 동적 URL로 변경 - DB 연결 하드코딩 → .env 기반으로 변경 - MySQL strict mode DATE 오류 수정
1674 lines
64 KiB
PHP
1674 lines
64 KiB
PHP
<?php
|
||
require_once($_SERVER['DOCUMENT_ROOT'] . "/session.php");
|
||
|
||
if(!isset($_SESSION["level"]) || $_SESSION["level"]>5) {
|
||
sleep(1);
|
||
header("Location:" . $WebSite . "login/login_form_form.php");
|
||
exit;
|
||
}
|
||
|
||
include $_SERVER['DOCUMENT_ROOT'] . '/load_header.php';
|
||
$title_message = '공사 인수 인계 보고서';
|
||
?>
|
||
<title><?=$title_message?></title>
|
||
<link rel="stylesheet" href="../output/css/style.css">
|
||
<style>
|
||
/* 메인 테이블 스타일 */
|
||
.main-table {
|
||
width: 100%;
|
||
border-collapse: collapse;
|
||
margin: 20px 0;
|
||
font-size: 14px;
|
||
}
|
||
|
||
.main-table td, .main-table th {
|
||
border: 1px solid #000;
|
||
padding: 5px;
|
||
vertical-align: middle;
|
||
}
|
||
|
||
.main-table th {
|
||
background-color: #f0f0f0;
|
||
font-weight: bold;
|
||
text-align: center;
|
||
}
|
||
|
||
/* 서명 관련 스타일 */
|
||
.signature-canvas {
|
||
border: 1px solid #ccc;
|
||
background: white;
|
||
cursor: crosshair;
|
||
}
|
||
.signature-container {
|
||
position: relative;
|
||
display: inline-block;
|
||
width: 100%;
|
||
}
|
||
.signature-remove {
|
||
position: absolute;
|
||
top: -4px;
|
||
right: -4px;
|
||
background: #dc3545;
|
||
color: white;
|
||
border: none;
|
||
border-radius: 50%;
|
||
width: 12px;
|
||
height: 12px;
|
||
font-size: 8px;
|
||
cursor: pointer;
|
||
display: none;
|
||
z-index: 10;
|
||
box-shadow: 0 1px 2px rgba(0,0,0,0.2);
|
||
}
|
||
.signature-remove:hover {
|
||
background: #c82333;
|
||
}
|
||
.signature-btn {
|
||
background: #007bff;
|
||
color: white;
|
||
border: none;
|
||
padding: 5px 10px;
|
||
border-radius: 3px;
|
||
cursor: pointer;
|
||
font-size: 12px;
|
||
}
|
||
.signature-btn:hover {
|
||
background: #0056b3;
|
||
}
|
||
|
||
/* 입력 필드 스타일 */
|
||
.form-control-borderless {
|
||
border: none;
|
||
background: transparent;
|
||
width: 100%;
|
||
padding: 2px;
|
||
}
|
||
|
||
/* 자동 크기 조정 textarea */
|
||
.auto-resize {
|
||
resize: none;
|
||
overflow: hidden;
|
||
min-height: 40px;
|
||
transition: height 0.1s ease;
|
||
}
|
||
|
||
/* 모드별 스타일 */
|
||
.view-mode .form-control-borderless,
|
||
.view-mode input,
|
||
.view-mode textarea {
|
||
border: none !important;
|
||
background: transparent !important;
|
||
pointer-events: none !important;
|
||
cursor: default !important;
|
||
color: #000 !important;
|
||
}
|
||
|
||
.edit-mode .form-control-borderless,
|
||
.insert-mode .form-control-borderless,
|
||
.edit-mode input,
|
||
.insert-mode input,
|
||
.edit-mode textarea,
|
||
.insert-mode textarea {
|
||
border: 1px solid #ced4da !important;
|
||
background: white !important;
|
||
pointer-events: auto !important;
|
||
cursor: text !important;
|
||
}
|
||
|
||
.view-mode .dynamic-button {
|
||
display: none !important;
|
||
}
|
||
|
||
.edit-mode .dynamic-button,
|
||
.insert-mode .dynamic-button {
|
||
display: inline-block !important;
|
||
}
|
||
|
||
/* 조회모드에서 모든 버튼 숨김 (PDF 생성시에도 포함) */
|
||
.view-mode .signature-btn {
|
||
display: none !important;
|
||
}
|
||
|
||
.view-mode .signature-remove {
|
||
display: none !important;
|
||
}
|
||
|
||
/* 테이블 내부의 모든 버튼과 X 버튼 숨김 */
|
||
.view-mode .main-table button,
|
||
.view-mode .main-table .btn,
|
||
.view-mode .dynamic-table button,
|
||
.view-mode .dynamic-table .btn,
|
||
.view-mode button[onclick*="remove"],
|
||
.view-mode button[onclick*="clear"],
|
||
.view-mode button[onclick*="add"],
|
||
.view-mode button[onclick*="signature"] {
|
||
display: none !important;
|
||
}
|
||
|
||
/* 모든 동적 버튼과 X 표시 버튼 숨김 */
|
||
.view-mode .dynamic-button,
|
||
.view-mode button:contains("×"),
|
||
.view-mode button[type="button"] {
|
||
display: none !important;
|
||
}
|
||
|
||
/* PDF 인쇄 시에도 버튼 숨김 */
|
||
@media print {
|
||
.view-mode button,
|
||
.view-mode .btn,
|
||
.signature-btn,
|
||
.signature-remove,
|
||
.dynamic-button {
|
||
display: none !important;
|
||
}
|
||
}
|
||
|
||
/* PDF 생성 시 회의 참석자 '기능' 열 숨김 */
|
||
.pdf-mode .attendee-function-column {
|
||
display: none !important;
|
||
}
|
||
|
||
/* PDF 생성 시 input 스타일 개선 */
|
||
.pdf-mode input,
|
||
.pdf-mode textarea {
|
||
border: none !important;
|
||
background: transparent !important;
|
||
outline: none !important;
|
||
box-shadow: none !important;
|
||
padding: 0 !important;
|
||
margin: 0 !important;
|
||
}
|
||
|
||
/* 조회모드에서 form-control 클래스도 비활성화 */
|
||
.view-mode .form-control {
|
||
border: none !important;
|
||
background: transparent !important;
|
||
pointer-events: none !important;
|
||
cursor: default !important;
|
||
color: #000 !important;
|
||
}
|
||
|
||
.edit-mode .form-control,
|
||
.insert-mode .form-control {
|
||
border: 1px solid #ced4da !important;
|
||
background: white !important;
|
||
pointer-events: auto !important;
|
||
}
|
||
|
||
/* 동적 테이블 스타일 */
|
||
.dynamic-table {
|
||
width: 100%;
|
||
border-collapse: collapse;
|
||
}
|
||
|
||
.dynamic-table td {
|
||
border: 1px solid #000;
|
||
padding: 3px;
|
||
}
|
||
|
||
.btn-add-row {
|
||
padding: 2px 8px;
|
||
font-size: 12px;
|
||
}
|
||
|
||
/* 회의 참석자 서명 영역 */
|
||
.attendee-signature {
|
||
min-height: 40px;
|
||
border: 1px solid #ddd;
|
||
padding: 2px;
|
||
}
|
||
</style>
|
||
</head>
|
||
<body class="<?php echo $mode; ?>-mode">
|
||
<?php
|
||
// 권한 확인
|
||
$ApprovalAdmin = false;
|
||
|
||
// 현재 날짜
|
||
$today = date('Y-m-d');
|
||
$todayStr = date('y.m.d');
|
||
|
||
// GET 파라미터 확인
|
||
$work_num = isset($_GET['num']) ? $_GET['num'] : '';
|
||
$handover_num = isset($_GET['handover_num']) ? $_GET['handover_num'] : '';
|
||
|
||
// write_form.php에서 전달받은 초기값들
|
||
$init_workplacename = isset($_GET['workplacename']) ? $_GET['workplacename'] : '';
|
||
$init_contract_amount = isset($_GET['contract_amount']) ? $_GET['contract_amount'] : '';
|
||
$init_firstord = isset($_GET['firstord']) ? $_GET['firstord'] : '';
|
||
$init_secondord = isset($_GET['secondord']) ? $_GET['secondord'] : '';
|
||
// 디버깅을 위한 로그
|
||
error_log("handover_doc.php - work_num: " . $work_num . ", handover_num: " . $handover_num);
|
||
|
||
// work_num 유효성 검사
|
||
// if (empty($work_num) || !is_numeric($work_num)) {
|
||
// echo "<script>alert('유효하지 않은 작업 번호입니다.'); window.close();</script>";
|
||
// exit;
|
||
// }
|
||
|
||
// 모드 설정: handover_num이 0보다 크면 조회모드, 그렇지 않으면 insert모드
|
||
$mode = ($handover_num && intval($handover_num) > 0) ? 'view' : 'insert';
|
||
|
||
// URL에서 mode 파라미터가 있으면 우선 적용
|
||
if (isset($_GET['mode']) && $_GET['mode'] == 'edit') {
|
||
$mode = 'edit';
|
||
}
|
||
|
||
// 데이터베이스 연결 (PDO 사용)
|
||
require_once($_SERVER['DOCUMENT_ROOT'] . "/lib/mydb.php");
|
||
$pdo = db_connect();
|
||
|
||
// 기존 데이터 로드
|
||
$handover_data = null;
|
||
if ($handover_num) {
|
||
try {
|
||
$sql = "SELECT * FROM work_handover WHERE num = ? AND is_deleted = 'N'";
|
||
$stmh = $pdo->prepare($sql);
|
||
$stmh->bindValue(1, $handover_num, PDO::PARAM_INT);
|
||
$stmh->execute();
|
||
|
||
if ($stmh->rowCount() > 0) {
|
||
$handover_data = $stmh->fetch(PDO::FETCH_ASSOC);
|
||
}
|
||
|
||
// echo '<pre>';
|
||
// print_r($handover_data['contract_amount']);
|
||
// echo '</pre>';
|
||
$contract_amount = $handover_data['contract_amount'] ?? '';
|
||
|
||
} catch (PDOException $Exception) {
|
||
print "오류: " . $Exception->getMessage();
|
||
}
|
||
}
|
||
|
||
// work 테이블에서 기본 정보 로드
|
||
$work_data = null;
|
||
|
||
if ($work_num) {
|
||
try {
|
||
$sql = "SELECT * FROM work WHERE num = ?";
|
||
$stmh = $pdo->prepare($sql);
|
||
$stmh->bindValue(1, $work_num, PDO::PARAM_INT);
|
||
$stmh->execute();
|
||
|
||
if ($stmh->rowCount() > 0) {
|
||
$work_data = $stmh->fetch(PDO::FETCH_ASSOC);
|
||
}
|
||
} catch (PDOException $Exception) {
|
||
print "오류: " . $Exception->getMessage();
|
||
}
|
||
}
|
||
|
||
|
||
|
||
?>
|
||
|
||
<!-- 상단 버튼 영역 -->
|
||
<div class="container mt-2 mb-2">
|
||
<div class="d-flex align-items-center justify-content-end mt-1 m-2">
|
||
<?php if ($mode == 'view'): ?>
|
||
<button class="btn btn-primary btn-sm me-1 ms-1" onclick="switchToEditMode()">
|
||
<i class="bi bi-pencil-fill"></i> 수정
|
||
</button>
|
||
<button class="btn btn-danger btn-sm me-1 ms-1" onclick="deleteHandover()">
|
||
<i class="bi bi-trash-fill"></i> 삭제
|
||
</button>
|
||
<button class="btn btn-success btn-sm me-1 ms-1" onclick="generatePDF()">
|
||
<i class="bi bi-file-earmark-pdf-fill"></i> PDF 저장
|
||
</button>
|
||
<?php else: ?>
|
||
<button class="btn btn-dark btn-sm me-1 ms-1 saveData">
|
||
<i class="bi bi-floppy2-fill"></i> 저장
|
||
</button>
|
||
<?php if ($mode == 'edit'): ?>
|
||
<button class="btn btn-secondary btn-sm me-1 ms-1" onclick="switchToViewMode()">
|
||
<i class="bi bi-eye-fill"></i> 조회모드
|
||
</button>
|
||
<?php endif; ?>
|
||
<?php endif; ?>
|
||
<button class="btn btn-secondary btn-sm me-1 ms-5" onclick="self.close();">
|
||
<i class="bi bi-x-lg"></i> 닫기
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div id="content-to-print">
|
||
<div class="container-fluid">
|
||
<form id="handoverForm">
|
||
<input type="hidden" name="work_num" value="<?php echo htmlspecialchars($work_num); ?>">
|
||
<input type="hidden" name="handover_num" value="<?php echo htmlspecialchars($handover_num); ?>">
|
||
<input type="hidden" id="contract_items_json" name="contract_items_json" value="">
|
||
<table>
|
||
<tbody>
|
||
<tr>
|
||
<td rowspan="3" style="width: 60%;">
|
||
<div class="row">
|
||
<div class="col-sm-2">
|
||
<img src="../img/companylogo1.png" alt="경동기업" class="me-1" style="width:150%; height:auto;">
|
||
</div>
|
||
<div class="col-sm-10">
|
||
<div class="d-flex align-items-center justify-content-center m-1">
|
||
<span class="text-dark ms-2 me-1 fs-2" > 공사 인수인계 보고서 </span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</td>
|
||
<td class="text-center p-1" style="width : 150px; height:22px; padding:2px;">계약담당</td>
|
||
<td class="text-center p-1" style="width : 150px; height:22px; padding:2px;">공사PM</td>
|
||
<td class="text-center p-1" style="width : 150px; height:22px; padding:2px;">부서장</td>
|
||
<td class="text-center p-1" style="width : 150px; height:22px; padding:2px;">대표</td>
|
||
</tr>
|
||
<tr style="height:40px;">
|
||
<td class="text-center">
|
||
<div id="contractManagerDiv" class="signature-container">
|
||
<div id="contractManagerSignature" style="min-height: 30px; border: 1px solid #ddd; margin-bottom: 5px; position: relative;">
|
||
<?php if ($handover_data && !empty($handover_data['contract_manager_signature']) && strlen(trim($handover_data['contract_manager_signature'])) > 10): ?>
|
||
<img src="data:image/png;base64,<?php echo htmlspecialchars($handover_data['contract_manager_signature']); ?>" style="max-width: 100%; max-height: 30px;">
|
||
<button type="button" class="signature-remove" onclick="removeSignature('contractManager')" style="display: block;">×</button>
|
||
<?php endif; ?>
|
||
</div>
|
||
<button type="button" class="signature-btn" onclick="openSignatureModal('contractManager')">서명</button>
|
||
<input type="hidden" name="contract_manager_signature" value="<?php echo $handover_data ? $handover_data['contract_manager_signature'] : ''; ?>">
|
||
<br>
|
||
<input type="text" class="form-control" name="contract_manager_date" value="<?php echo $handover_data ? $handover_data['contract_manager_date'] : ''; ?>" placeholder="날짜">
|
||
</div>
|
||
</td>
|
||
<td class="text-center">
|
||
<div id="constructionPMDiv" class="signature-container">
|
||
<div id="constructionPMSignature" style="min-height: 30px; border: 1px solid #ddd; margin-bottom: 5px; position: relative;">
|
||
<?php if ($handover_data && !empty($handover_data['construction_pm_signature']) && strlen(trim($handover_data['construction_pm_signature'])) > 10): ?>
|
||
<img src="data:image/png;base64,<?php echo htmlspecialchars($handover_data['construction_pm_signature']); ?>" style="max-width: 100%; max-height: 30px;">
|
||
<button type="button" class="signature-remove" onclick="removeSignature('constructionPM')" style="display: block;">×</button>
|
||
<?php endif; ?>
|
||
</div>
|
||
<button type="button" class="signature-btn" onclick="openSignatureModal('constructionPM')">서명</button>
|
||
<input type="hidden" name="construction_pm_signature" value="<?php echo $handover_data ? $handover_data['construction_pm_signature'] : ''; ?>">
|
||
<br>
|
||
<input type="text" class="form-control" name="construction_pm_date" value="<?php echo $handover_data ? $handover_data['construction_pm_date'] : ''; ?>" placeholder="날짜">
|
||
</div>
|
||
</td>
|
||
<td class="text-center">
|
||
<div id="departmentHeadDiv" class="signature-container">
|
||
<div id="departmentHeadSignature" style="min-height: 30px; border: 1px solid #ddd; margin-bottom: 5px; position: relative;">
|
||
<?php if ($handover_data && !empty($handover_data['department_head_signature']) && strlen(trim($handover_data['department_head_signature'])) > 10): ?>
|
||
<img src="data:image/png;base64,<?php echo htmlspecialchars($handover_data['department_head_signature']); ?>" style="max-width: 100%; max-height: 30px;">
|
||
<button type="button" class="signature-remove" onclick="removeSignature('departmentHead')" style="display: block;">×</button>
|
||
<?php endif; ?>
|
||
</div>
|
||
<button type="button" class="signature-btn" onclick="openSignatureModal('departmentHead')">서명</button>
|
||
<input type="hidden" name="department_head_signature" value="<?php echo $handover_data ? $handover_data['department_head_signature'] : ''; ?>">
|
||
<br>
|
||
<input type="text" class="form-control" name="department_head_date" value="<?php echo $handover_data ? $handover_data['department_head_date'] : ''; ?>" placeholder="날짜">
|
||
</div>
|
||
</td>
|
||
<td class="text-center">
|
||
<div id="ceoDiv" class="signature-container">
|
||
<div id="ceoSignature" style="min-height: 30px; border: 1px solid #ddd; margin-bottom: 5px; position: relative;">
|
||
<?php if ($handover_data && !empty($handover_data['ceo_signature']) && strlen(trim($handover_data['ceo_signature'])) > 10): ?>
|
||
<img src="data:image/png;base64,<?php echo htmlspecialchars($handover_data['ceo_signature']); ?>" style="max-width: 100%; max-height: 30px;">
|
||
<button type="button" class="signature-remove" onclick="removeSignature('ceo')" style="display: block;">×</button>
|
||
<?php endif; ?>
|
||
</div>
|
||
<button type="button" class="signature-btn" onclick="openSignatureModal('ceo')">서명</button>
|
||
<input type="hidden" name="ceo_signature" value="<?php echo $handover_data ? $handover_data['ceo_signature'] : ''; ?>">
|
||
<br>
|
||
<input type="text" class="form-control" name="ceo_date" value="<?php echo $handover_data ? $handover_data['ceo_date'] : ''; ?>" placeholder="날짜">
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
|
||
<!-- 서명 모달 -->
|
||
<div class="modal fade" id="signatureModal" tabindex="-1" aria-labelledby="signatureModalLabel" aria-hidden="true">
|
||
<div class="modal-dialog modal-lg">
|
||
<div class="modal-content">
|
||
<div class="modal-header">
|
||
<h5 class="modal-title" id="signatureModalLabel">서명</h5>
|
||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||
</div>
|
||
<div class="modal-body">
|
||
<div class="text-center mb-3">
|
||
<canvas id="signatureCanvas" class="signature-canvas" width="350" height="200"></canvas>
|
||
</div>
|
||
<div class="text-center">
|
||
<button type="button" class="btn btn-secondary me-2" onclick="clearCanvas()">지우기</button>
|
||
<button type="button" class="btn btn-primary" onclick="saveSignature()">확인</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 메인 테이블 -->
|
||
<div class="container-fluid mt-3 mb-4">
|
||
<table class="main-table">
|
||
<tbody>
|
||
<!-- 현장명 -->
|
||
<tr>
|
||
<th style="width: 15%;">현장명</th>
|
||
<td colspan="3">
|
||
<input type="text" class="form-control-borderless text-start" name="workplacename" value="<?php echo $handover_data ? $handover_data['workplacename'] : ($work_data ? $work_data['workplacename'] : $init_workplacename); ?>">
|
||
</td>
|
||
</tr>
|
||
|
||
<!-- 발주처 -->
|
||
<tr>
|
||
<th>발주처</th>
|
||
<td colspan="3">
|
||
<input type="text" class="form-control-borderless text-start" name="firstord" value="<?php echo $handover_data ? $handover_data['firstord'] : ($work_data ? $work_data['firstord'] : $init_firstord); ?>">
|
||
</td>
|
||
</tr>
|
||
|
||
<!-- 시공사 -->
|
||
<tr>
|
||
<th>시공사</th>
|
||
<td colspan="3">
|
||
<input type="text" class="form-control-borderless text-start" name="secondord" value="<?php echo $handover_data ? $handover_data['secondord'] : ($work_data ? $work_data['secondord'] : $init_secondord); ?>">
|
||
</td>
|
||
</tr>
|
||
|
||
<!-- 준공 및 계약금액 -->
|
||
<tr>
|
||
<th rowspan="1" >준공</th>
|
||
<td style="width: 30%;">
|
||
<input type="month" class="form-control-borderless" name="completion_date" value="<?php
|
||
$completion_date = $handover_data ? $handover_data['completion_date'] : '';
|
||
// YYYY-MM 형식으로 변환
|
||
if (!empty($completion_date)) {
|
||
if (preg_match('/^\d{4}-\d{2}$/', $completion_date)) {
|
||
echo $completion_date;
|
||
} elseif (preg_match('/^\d{4}-\d{2}-\d{2}$/', $completion_date)) {
|
||
echo substr($completion_date, 0, 7); // YYYY-MM-DD -> YYYY-MM
|
||
}
|
||
}
|
||
?>">
|
||
</td>
|
||
<th style="width: 30%;">계약금액<br>
|
||
<input name="priceIncludesVat" id="priceIncludesVat" class="form-control-sm w100px p-0" value="<?php echo $handover_data ? $handover_data['priceIncludesVat'] : '공급가액'; ?>">
|
||
</th>
|
||
<td style="width: 30%;">
|
||
₩ <input type="text" class="form-control-borderless text-start"
|
||
style="display: inline-block; width: calc(100% - 20px);"
|
||
name="contract_amount"
|
||
id="contract_amount"
|
||
value="<?php
|
||
// $handover_data가 있을 때는 부가세 포함금액이므로 공급가액(10% 부가세 제외)로 변환
|
||
if (!empty($contract_amount) && is_numeric(str_replace(',', '', $contract_amount))) {
|
||
echo number_format((float)str_replace(',', '', $contract_amount));
|
||
}
|
||
else {
|
||
// 입력값이 부가세 포함(총액)일 경우, 공급가액(부가세 10% 제외)로 변환하여 표시
|
||
$amount = $init_contract_amount;
|
||
$amount = str_replace(',', '', $amount);
|
||
if (is_numeric($amount)) {
|
||
// 공급가액 = 총액 / 1.1 (부가세 10% 제외)
|
||
$supply_amount = $amount > 0 ? floor($amount / 1.1) : '';
|
||
// 원단위가 99로 끝나면 +1 처리
|
||
if ($supply_amount !== '' && substr($supply_amount, -2) === '99') {
|
||
$supply_amount = $supply_amount + 1;
|
||
}
|
||
echo $supply_amount !== '' ? number_format($supply_amount) : '';
|
||
} else {
|
||
echo htmlspecialchars($init_contract_amount);
|
||
}
|
||
}
|
||
?>"
|
||
oninput="this.value = this.value.replace(/[^0-9]/g, ''); formatAmountInput(this);"
|
||
autocomplete="off"
|
||
>
|
||
</td>
|
||
<script>
|
||
// 3자리마다 콤마 자동 입력 함수
|
||
function formatAmountInput(el) {
|
||
var val = el.value.replace(/[^0-9]/g, '');
|
||
if(val === '') {
|
||
el.value = '';
|
||
return;
|
||
}
|
||
el.value = val.replace(/\B(?=(\d{3})+(?!\d))/g, ",");
|
||
}
|
||
// 페이지 로드 시 기존 값도 콤마 적용
|
||
document.addEventListener('DOMContentLoaded', function() {
|
||
var amt = document.getElementById('contract_amount');
|
||
if(amt) formatAmountInput(amt);
|
||
});
|
||
</script>
|
||
</tr>
|
||
|
||
<!-- 계약 ITEM (동적 테이블) -->
|
||
<tr>
|
||
<th >계약<br>ITEM</th>
|
||
<td colspan="3" style="padding: 0;">
|
||
<div style="text-align: right; padding: 5px;">
|
||
<button type="button" class="btn btn-sm btn-outline-primary btn-add-row dynamic-button" onclick="addContractItem()">
|
||
<i class="bi bi-plus-circle"></i> 행 추가
|
||
</button>
|
||
</div>
|
||
<table id="contractItemTable" class="dynamic-table">
|
||
<!-- 구분 및 수량, 비고 -->
|
||
<thead class="table-secondary">
|
||
<tr>
|
||
<td style="width: 33%;">구분</td>
|
||
<td style="width: 10%;">수량</td>
|
||
<td style="width: 54%;">비고</td>
|
||
</tr>
|
||
</thead>
|
||
<tbody id="contractItemTableBody">
|
||
<!-- 동적 행들이 여기에 추가됩니다 -->
|
||
</tbody>
|
||
</tr>
|
||
</table>
|
||
</td>
|
||
</tr>
|
||
<!-- 집행유무 -->
|
||
<tr>
|
||
<th>집행유무</th>
|
||
<td colspan="3">
|
||
<div style="display: flex; gap: 10px;">
|
||
<div style="flex: 1;">
|
||
<label>2차 배관 유무:</label>
|
||
<input type="text" class="form-control-borderless" style="display: inline-block; width: calc(100% - 100px);" name="secondary_piping" value="<?php echo $handover_data ? $handover_data['secondary_piping'] : ''; ?>">
|
||
</div>
|
||
<div style="flex: 1;">
|
||
<label>도장 & 코킹 유무:</label>
|
||
<input type="text" class="form-control-borderless" style="display: inline-block; width: calc(100% - 120px);" name="painting_caulking" value="<?php echo $handover_data ? $handover_data['painting_caulking'] : ''; ?>">
|
||
</div>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
|
||
<!-- 장비 外 실행금액 -->
|
||
<tr>
|
||
<th rowspan="2">장비 外<br>실행금액</th>
|
||
<td colspan="3">
|
||
<textarea class="form-control-borderless auto-resize" name="equipment_cost" style="width: 100%; resize: none; overflow: hidden; min-height: 40px;"><?php echo $handover_data ? $handover_data['equipment_cost'] : ''; ?></textarea>
|
||
</td>
|
||
</tr>
|
||
|
||
<!-- 특이사항 -->
|
||
<tr>
|
||
<th>특이사항</th>
|
||
<td colspan="3">
|
||
<textarea class="form-control-borderless auto-resize" name="special_notes" style="width: 100%; resize: none; overflow: hidden; min-height: 40px;"><?php echo $handover_data ? $handover_data['special_notes'] : ''; ?></textarea>
|
||
</td>
|
||
</tr>
|
||
<tr>
|
||
<tr>
|
||
<th rowspan="1">회의 참석자</th>
|
||
<td colspan="3" style="padding: 0;">
|
||
<div style="text-align: right; padding: 5px;">
|
||
<button type="button" class="btn btn-sm btn-outline-primary btn-add-row dynamic-button" onclick="addAttendee()">
|
||
<i class="bi bi-plus-circle"></i> 참석자 추가
|
||
</button>
|
||
</div>
|
||
<table id="attendeeTable" class="dynamic-table">
|
||
<!-- 회의 참석자 (동적 테이블) -->
|
||
<thead>
|
||
<tr>
|
||
<th style="width: 10%;">성명</th>
|
||
<th class="attendee-function-column" style="width: 15%;">기능</th>
|
||
<th style="width: 20%;">서명</th>
|
||
<th style="width: 55%;">미이행 사유</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody id="attendeeTableBody">
|
||
<!-- 동적 행들이 여기에 추가됩니다 -->
|
||
</tbody>
|
||
</table>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
|
||
<script>
|
||
$(document).ready(function(){
|
||
var loader = document.getElementById('loadingOverlay');
|
||
if(loader)
|
||
loader.style.display = 'none';
|
||
});
|
||
|
||
// 수정 모드로 전환
|
||
function switchToEditMode() {
|
||
const workNum = '<?php echo $work_num; ?>';
|
||
const handoverNum = '<?php echo $handover_num; ?>';
|
||
window.location.href = `handover_doc.php?num=${workNum}&handover_num=${handoverNum}&mode=edit`;
|
||
}
|
||
|
||
// 조회 모드로 전환
|
||
function switchToViewMode() {
|
||
const workNum = '<?php echo $work_num; ?>';
|
||
const handoverNum = '<?php echo $handover_num; ?>';
|
||
window.location.href = `handover_doc.php?num=${workNum}&handover_num=${handoverNum}`;
|
||
}
|
||
|
||
function saveHandover() {
|
||
// 폼 데이터 수집
|
||
const formData = new FormData(document.getElementById('handoverForm'));
|
||
|
||
// 계약 ITEM 데이터 수집 (동적 테이블에서)
|
||
const contractItems = collectContractItems();
|
||
|
||
// 회의 참석자 데이터 수집 (동적 테이블에서)
|
||
const meetingAttendees = collectAttendees();
|
||
|
||
// 서명 데이터 수집 (상단 결재 서명들)
|
||
const signatureData = {
|
||
contract_manager_signature: formData.get('contract_manager_signature') || '',
|
||
contract_manager_date: formData.get('contract_manager_date') || '',
|
||
construction_pm_signature: formData.get('construction_pm_signature') || '',
|
||
construction_pm_date: formData.get('construction_pm_date') || '',
|
||
department_head_signature: formData.get('department_head_signature') || '',
|
||
department_head_date: formData.get('department_head_date') || '',
|
||
ceo_signature: formData.get('ceo_signature') || '',
|
||
ceo_date: formData.get('ceo_date') || ''
|
||
};
|
||
|
||
// work_num 값 확인
|
||
const workNum = formData.get('work_num') || '<?php echo $work_num; ?>';
|
||
const handoverNum = formData.get('handover_num') || '<?php echo $handover_num; ?>';
|
||
|
||
console.log('work_num:', workNum);
|
||
console.log('handover_num:', handoverNum);
|
||
|
||
// work_num이 없거나 유효하지 않은 경우는 신규 작업으로 처리
|
||
if (workNum === 'undefined' || workNum === 'null') {
|
||
workNum = '';
|
||
}
|
||
|
||
// 최종 데이터 구성
|
||
const finalData = {
|
||
work_num: workNum,
|
||
handover_num: handoverNum,
|
||
workplacename: formData.get('workplacename') || '',
|
||
firstord: formData.get('firstord') || '',
|
||
secondord: formData.get('secondord') || '',
|
||
completion_date: formData.get('completion_date') || '',
|
||
contract_amount: formData.get('contract_amount') || '',
|
||
contract_items: JSON.stringify(contractItems),
|
||
execution_status: '', // 기존 필드 유지
|
||
secondary_piping: formData.get('secondary_piping') || '',
|
||
painting_caulking: formData.get('painting_caulking') || '',
|
||
equipment_cost: formData.get('equipment_cost') || '',
|
||
special_notes: formData.get('special_notes') || '',
|
||
meeting_attendees: JSON.stringify(meetingAttendees),
|
||
// 상단 결재 서명 데이터
|
||
contract_manager_signature: signatureData.contract_manager_signature,
|
||
contract_manager_date: signatureData.contract_manager_date,
|
||
construction_pm_signature: signatureData.construction_pm_signature,
|
||
construction_pm_date: signatureData.construction_pm_date,
|
||
department_head_signature: signatureData.department_head_signature,
|
||
department_head_date: signatureData.department_head_date,
|
||
ceo_signature: signatureData.ceo_signature,
|
||
ceo_date: signatureData.ceo_date,
|
||
regist_user: '<?php echo isset($user_name) ? $user_name : $_SESSION["user_name"]; ?>',
|
||
priceIncludesVat: formData.get('priceIncludesVat') || ''
|
||
};
|
||
|
||
// 디버깅을 위한 콘솔 출력
|
||
console.log('PHP에서 받은 work_num:', '<?php echo $work_num; ?>');
|
||
console.log('PHP에서 받은 handover_num:', '<?php echo $handover_num; ?>');
|
||
console.log('저장할 데이터:', finalData);
|
||
console.log('계약 ITEM:', contractItems);
|
||
console.log('회의 참석자:', meetingAttendees);
|
||
console.log('서명 데이터:', signatureData);
|
||
console.log('FormData에서 가져온 서명들:');
|
||
console.log(' - contract_manager_signature:', formData.get('contract_manager_signature'));
|
||
console.log(' - construction_pm_signature:', formData.get('construction_pm_signature'));
|
||
console.log(' - department_head_signature:', formData.get('department_head_signature'));
|
||
console.log(' - ceo_signature:', formData.get('ceo_signature'));
|
||
|
||
// AJAX로 데이터 전송
|
||
fetch('insert_handover.php', {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
},
|
||
body: JSON.stringify(finalData)
|
||
})
|
||
.then(response => {
|
||
// 응답이 JSON인지 확인
|
||
const contentType = response.headers.get('content-type');
|
||
if (contentType && contentType.includes('application/json')) {
|
||
return response.json();
|
||
} else {
|
||
// JSON이 아닌 경우 텍스트로 받아서 로그 출력
|
||
return response.text().then(text => {
|
||
console.error('Non-JSON response:', text);
|
||
throw new Error('서버에서 유효하지 않은 응답을 받았습니다.');
|
||
});
|
||
}
|
||
})
|
||
.then(data => {
|
||
if (data.success) {
|
||
Swal.fire({
|
||
title: '성공',
|
||
text: '인수인계 보고서가 저장되었습니다.',
|
||
icon: 'success',
|
||
confirmButtonText: '확인'
|
||
}).then(() => {
|
||
// 저장 후 조회모드로 리다이렉션 (응답에서 받은 work_num 사용)
|
||
const workNum = data.work_num || finalData.work_num;
|
||
const handoverNum = data.handover_num;
|
||
window.location.href = `handover_doc.php?num=${workNum}&handover_num=${handoverNum}`;
|
||
});
|
||
} else {
|
||
Swal.fire({
|
||
title: '오류',
|
||
text: '저장 중 오류가 발생했습니다: ' + (data.message || '알 수 없는 오류'),
|
||
icon: 'error',
|
||
confirmButtonText: '확인'
|
||
});
|
||
}
|
||
})
|
||
.catch(error => {
|
||
console.error('Error:', error);
|
||
Swal.fire({
|
||
title: '오류',
|
||
text: '저장 중 오류가 발생했습니다.',
|
||
icon: 'error',
|
||
confirmButtonText: '확인'
|
||
});
|
||
});
|
||
}
|
||
|
||
function printDocument() {
|
||
window.print();
|
||
}
|
||
|
||
function exportPDF() {
|
||
// PDF 내보내기 기능 (추후 구현)
|
||
alert('PDF 내보내기 기능은 추후 구현 예정입니다.');
|
||
}
|
||
|
||
function resetForm() {
|
||
if (confirm('모든 입력 내용을 초기화하시겠습니까?')) {
|
||
document.getElementById('handoverForm').reset();
|
||
}
|
||
}
|
||
|
||
function deleteHandover() {
|
||
Swal.fire({
|
||
title: '삭제 확인',
|
||
text: '인수인계 보고서를 삭제하시겠습니까?\n삭제된 데이터는 복구할 수 없습니다.',
|
||
icon: 'warning',
|
||
showCancelButton: true,
|
||
confirmButtonText: '삭제',
|
||
cancelButtonText: '취소',
|
||
confirmButtonColor: '#dc3545',
|
||
reverseButtons: true
|
||
}).then((result) => {
|
||
if (result.isConfirmed) {
|
||
const handoverNum = '<?php echo $handover_num; ?>';
|
||
|
||
// 삭제 진행 중 표시
|
||
Swal.fire({
|
||
title: '삭제 중...',
|
||
text: '잠시만 기다려주세요.',
|
||
allowOutsideClick: false,
|
||
allowEscapeKey: false,
|
||
showConfirmButton: false,
|
||
didOpen: () => {
|
||
Swal.showLoading();
|
||
}
|
||
});
|
||
|
||
fetch('delete_handover.php', {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
},
|
||
body: JSON.stringify({ handover_num: handoverNum })
|
||
})
|
||
.then(response => {
|
||
const contentType = response.headers.get('content-type');
|
||
if (contentType && contentType.includes('application/json')) {
|
||
return response.json();
|
||
} else {
|
||
return response.text().then(text => {
|
||
console.error('Non-JSON response:', text);
|
||
throw new Error('서버에서 유효하지 않은 응답을 받았습니다.');
|
||
});
|
||
}
|
||
})
|
||
.then(data => {
|
||
if (data.success) {
|
||
Swal.fire({
|
||
title: '삭제 완료',
|
||
text: '인수인계 보고서가 삭제되었습니다.',
|
||
icon: 'success',
|
||
confirmButtonText: '확인'
|
||
}).then(() => {
|
||
// 삭제 후 창 닫기 또는 목록으로 이동
|
||
window.close();
|
||
});
|
||
} else {
|
||
Swal.fire({
|
||
title: '삭제 실패',
|
||
text: '삭제 중 오류가 발생했습니다: ' + (data.message || '알 수 없는 오류'),
|
||
icon: 'error',
|
||
confirmButtonText: '확인'
|
||
});
|
||
}
|
||
})
|
||
.catch(error => {
|
||
console.error('Error:', error);
|
||
Swal.fire({
|
||
title: '오류',
|
||
text: '삭제 중 오류가 발생했습니다.',
|
||
icon: 'error',
|
||
confirmButtonText: '확인'
|
||
});
|
||
});
|
||
}
|
||
});
|
||
}
|
||
|
||
// viewBendingWork.php 참조 기능들
|
||
$(document).ready(function() {
|
||
// 품질 승인 버튼 클릭 이벤트
|
||
$('.approvalBtn').click(function() {
|
||
// 승인자의 이름과 오늘 날짜 가져오기
|
||
const userName = '<?=$user_name?>'; // PHP로부터 가져온 사용자 이름
|
||
const todayStr = '<?=$todayStr?>'; // PHP로부터 가져온 오늘 날짜
|
||
|
||
// 승인 정보를 approvalDiv에 표시
|
||
$('#approvalDiv').html(
|
||
'<input type="text" class="form-control" name="approver_name" value="' + userName + '" readonly><br>' +
|
||
'<input type="text" class="form-control" name="approver_date" value="' + todayStr + '" readonly>'
|
||
);
|
||
$('#approvalDiv').show();
|
||
|
||
// 검토 정보도 표시
|
||
$('#reviewDiv').html(
|
||
'<input type="text" class="form-control" name="reviewer_name" value="' + userName + '" readonly><br>' +
|
||
'<input type="text" class="form-control" name="reviewer_date" value="' + todayStr + '" readonly>'
|
||
);
|
||
$('#reviewDiv').show();
|
||
});
|
||
|
||
// 서버 저장 버튼 클릭 이벤트
|
||
$('.saveData').off('click').on('click', function() {
|
||
saveHandover();
|
||
});
|
||
});
|
||
|
||
// PDF 생성 함수
|
||
function generatePDF() {
|
||
var workplace = '<?php echo $handover_data ? htmlspecialchars($handover_data["workplacename"], ENT_QUOTES) : ""; ?>';
|
||
var deadline = '<?php echo $handover_data ? htmlspecialchars($handover_data["completion_date"], ENT_QUOTES) : ""; ?>';
|
||
var result = 'JUIL인수인계_보고서(' + workplace + ')' + deadline + '.pdf';
|
||
|
||
var element = document.getElementById('content-to-print');
|
||
|
||
// PDF 생성 전 처리
|
||
preparePDFMode();
|
||
|
||
var opt = {
|
||
margin: [10, 3, 12, 3], // Top, right, bottom, left margins
|
||
filename: result,
|
||
image: { type: 'jpeg', quality: 0.98 },
|
||
html2canvas: {
|
||
scale: 1,
|
||
useCORS: true,
|
||
allowTaint: true
|
||
},
|
||
jsPDF: { unit: 'mm', format: 'a4', orientation: 'portrait' },
|
||
pagebreak: { mode: [''] }
|
||
};
|
||
|
||
// PDF 생성 및 완료 후 복원
|
||
html2pdf().from(element).set(opt).save().then(() => {
|
||
// PDF 생성 완료 후 원래 상태로 복원
|
||
restoreFromPDFMode();
|
||
}).catch((error) => {
|
||
console.error('PDF 생성 오류:', error);
|
||
// 오류 발생 시에도 복원
|
||
restoreFromPDFMode();
|
||
});
|
||
}
|
||
|
||
// 결재 취소 기능
|
||
$(document).on('click', '.remove-approval', function() {
|
||
// 결재 정보를 지우고 화면에서 숨김
|
||
$('#approvalDiv').html('').hide();
|
||
$('#reviewDiv').html('').hide();
|
||
|
||
Toastify({
|
||
text: "결재 정보가 삭제되었습니다.",
|
||
duration: 2000,
|
||
close: true,
|
||
gravity: "top",
|
||
position: "center",
|
||
style: {
|
||
background: "linear-gradient(to right, #ff5f6d, #ffc371)"
|
||
},
|
||
}).showToast();
|
||
});
|
||
|
||
function captureReturnKey(e) {
|
||
if(e.keyCode==13 && e.srcElement.type != 'textarea')
|
||
return false;
|
||
}
|
||
|
||
// 서명 관련 변수
|
||
let currentSignatureType = '';
|
||
let signatureCanvas = null;
|
||
let signatureContext = null;
|
||
let isDrawing = false;
|
||
let lastX = 0;
|
||
let lastY = 0;
|
||
|
||
// 서명 캔버스 초기화
|
||
function initSignatureCanvas() {
|
||
const canvas = document.getElementById('signatureCanvas');
|
||
signatureCanvas = canvas;
|
||
signatureContext = canvas.getContext('2d');
|
||
|
||
// 캔버스 스타일 설정
|
||
signatureContext.strokeStyle = '#000';
|
||
signatureContext.lineWidth = 2;
|
||
signatureContext.lineCap = 'round';
|
||
signatureContext.lineJoin = 'round';
|
||
|
||
// 이벤트 리스너 추가
|
||
canvas.addEventListener('mousedown', startDrawing);
|
||
canvas.addEventListener('mousemove', draw);
|
||
canvas.addEventListener('mouseup', stopDrawing);
|
||
canvas.addEventListener('mouseout', stopDrawing);
|
||
|
||
// 터치 이벤트 추가 (모바일 지원)
|
||
canvas.addEventListener('touchstart', handleTouchStart);
|
||
canvas.addEventListener('touchmove', handleTouchMove);
|
||
canvas.addEventListener('touchend', stopDrawing);
|
||
|
||
// 모달이 닫힐 때 캔버스 초기화
|
||
$('#signatureModal').on('hidden.bs.modal', function () {
|
||
if (signatureContext) {
|
||
signatureContext.clearRect(0, 0, signatureCanvas.width, signatureCanvas.height);
|
||
}
|
||
isDrawing = false;
|
||
});
|
||
}
|
||
|
||
// 마우스 이벤트 처리
|
||
function startDrawing(e) {
|
||
isDrawing = true;
|
||
const rect = signatureCanvas.getBoundingClientRect();
|
||
lastX = e.clientX - rect.left;
|
||
lastY = e.clientY - rect.top;
|
||
}
|
||
|
||
function draw(e) {
|
||
if (!isDrawing) return;
|
||
e.preventDefault();
|
||
|
||
const rect = signatureCanvas.getBoundingClientRect();
|
||
const currentX = e.clientX - rect.left;
|
||
const currentY = e.clientY - rect.top;
|
||
|
||
signatureContext.beginPath();
|
||
signatureContext.moveTo(lastX, lastY);
|
||
signatureContext.lineTo(currentX, currentY);
|
||
signatureContext.stroke();
|
||
|
||
lastX = currentX;
|
||
lastY = currentY;
|
||
}
|
||
|
||
function stopDrawing() {
|
||
isDrawing = false;
|
||
}
|
||
|
||
// 터치 이벤트 처리
|
||
function handleTouchStart(e) {
|
||
e.preventDefault();
|
||
const touch = e.touches[0];
|
||
const rect = signatureCanvas.getBoundingClientRect();
|
||
lastX = touch.clientX - rect.left;
|
||
lastY = touch.clientY - rect.top;
|
||
isDrawing = true;
|
||
}
|
||
|
||
function handleTouchMove(e) {
|
||
e.preventDefault();
|
||
if (!isDrawing) return;
|
||
|
||
const touch = e.touches[0];
|
||
const rect = signatureCanvas.getBoundingClientRect();
|
||
const currentX = touch.clientX - rect.left;
|
||
const currentY = touch.clientY - rect.top;
|
||
|
||
signatureContext.beginPath();
|
||
signatureContext.moveTo(lastX, lastY);
|
||
signatureContext.lineTo(currentX, currentY);
|
||
signatureContext.stroke();
|
||
|
||
lastX = currentX;
|
||
lastY = currentY;
|
||
}
|
||
|
||
// 캔버스 지우기
|
||
function clearCanvas() {
|
||
signatureContext.clearRect(0, 0, signatureCanvas.width, signatureCanvas.height);
|
||
}
|
||
|
||
// 서명 모달 열기
|
||
function openSignatureModal(signatureType) {
|
||
// 조회모드에서는 서명 모달을 열지 않음
|
||
const mode = '<?php echo $mode; ?>';
|
||
if (mode === 'view') {
|
||
return false;
|
||
}
|
||
|
||
currentSignatureType = signatureType;
|
||
|
||
// 모달 제목 설정
|
||
let title = '';
|
||
switch(signatureType) {
|
||
case 'contractManager':
|
||
title = '계약담당 서명';
|
||
break;
|
||
case 'constructionPM':
|
||
title = '공사PM 서명';
|
||
break;
|
||
case 'departmentHead':
|
||
title = '부서장 서명';
|
||
break;
|
||
case 'ceo':
|
||
title = '대표 서명';
|
||
break;
|
||
}
|
||
|
||
$('#signatureModalLabel').text(title);
|
||
$('#signatureModal').modal('show');
|
||
|
||
// 모달이 완전히 열린 후 캔버스 초기화
|
||
setTimeout(() => {
|
||
initSignatureCanvas();
|
||
// 캔버스 초기화 (이전 서명 지우기)
|
||
if (signatureContext) {
|
||
signatureContext.clearRect(0, 0, signatureCanvas.width, signatureCanvas.height);
|
||
}
|
||
}, 300);
|
||
}
|
||
|
||
// 서명 저장
|
||
function saveSignature() {
|
||
// 캔버스가 비어있는지 확인
|
||
const imageData = signatureContext.getImageData(0, 0, signatureCanvas.width, signatureCanvas.height);
|
||
const hasContent = imageData.data.some(channel => channel !== 0);
|
||
|
||
if (!hasContent) {
|
||
Swal.fire({
|
||
title: '알림',
|
||
text: '서명을 그려주세요.',
|
||
icon: 'warning',
|
||
confirmButtonText: '확인'
|
||
});
|
||
return;
|
||
}
|
||
|
||
// base64로 변환
|
||
const signatureDataUrl = signatureCanvas.toDataURL('image/png');
|
||
const base64Data = signatureDataUrl.split(',')[1]; // data:image/png;base64, 부분 제거
|
||
|
||
// 참석자 서명인 경우
|
||
if (currentSignatureType.startsWith('attendee_')) {
|
||
const attendeeIndex = currentSignatureType.split('_')[1];
|
||
|
||
// hidden input 업데이트
|
||
const hiddenInput = document.getElementById(`attendeeSignatureInput${attendeeIndex}`);
|
||
if (hiddenInput) {
|
||
hiddenInput.value = base64Data;
|
||
}
|
||
|
||
// 서명 이미지 표시
|
||
const signatureDisplay = document.getElementById(`attendeeSignatureDisplay${attendeeIndex}`);
|
||
if (signatureDisplay) {
|
||
signatureDisplay.innerHTML = `
|
||
<img src="${signatureDataUrl}" style="max-width: 100%; max-height: 40px;">
|
||
<button type="button" class="btn btn-sm btn-danger dynamic-button" onclick="clearAttendeeSignature(${attendeeIndex})" style="position: absolute; top: 2px; right: 2px; padding: 1px 4px; font-size: 10px;">×</button>
|
||
`;
|
||
}
|
||
} else {
|
||
// 기존 서명 처리 (계약담당, 공사PM 등)
|
||
let inputName = '';
|
||
switch(currentSignatureType) {
|
||
case 'contractManager':
|
||
inputName = 'contract_manager_signature';
|
||
break;
|
||
case 'constructionPM':
|
||
inputName = 'construction_pm_signature';
|
||
break;
|
||
case 'departmentHead':
|
||
inputName = 'department_head_signature';
|
||
break;
|
||
case 'ceo':
|
||
inputName = 'ceo_signature';
|
||
break;
|
||
}
|
||
|
||
const hiddenInput = document.querySelector(`input[name="${inputName}"]`);
|
||
if (hiddenInput) {
|
||
hiddenInput.value = base64Data;
|
||
}
|
||
|
||
// 서명 이미지 표시
|
||
const signatureDiv = document.getElementById(`${currentSignatureType}Signature`);
|
||
if (signatureDiv) {
|
||
signatureDiv.innerHTML = `
|
||
<img src="${signatureDataUrl}" style="max-width: 100%; max-height: 30px;">
|
||
<button type="button" class="signature-remove" onclick="removeSignature('${currentSignatureType}')" style="display: block;">×</button>
|
||
`;
|
||
}
|
||
|
||
// 날짜 자동 입력
|
||
let dateFieldName = '';
|
||
switch(currentSignatureType) {
|
||
case 'contractManager':
|
||
dateFieldName = 'contract_manager_date';
|
||
break;
|
||
case 'constructionPM':
|
||
dateFieldName = 'construction_pm_date';
|
||
break;
|
||
case 'departmentHead':
|
||
dateFieldName = 'department_head_date';
|
||
break;
|
||
case 'ceo':
|
||
dateFieldName = 'ceo_date';
|
||
break;
|
||
}
|
||
|
||
const dateInput = document.querySelector(`input[name="${dateFieldName}"]`);
|
||
if (dateInput) {
|
||
const today = new Date();
|
||
const year = today.getFullYear().toString().slice(-2); // 년도 뒤 2자리
|
||
const month = String(today.getMonth() + 1).padStart(2, '0'); // 월 2자리
|
||
const day = String(today.getDate()).padStart(2, '0'); // 일 2자리
|
||
const formattedDate = `${year}.${month}.${day}`;
|
||
dateInput.value = formattedDate;
|
||
}
|
||
}
|
||
|
||
// 모달 닫기
|
||
$('#signatureModal').modal('hide');
|
||
|
||
// 성공 메시지
|
||
Swal.fire({
|
||
title: '성공',
|
||
text: '서명이 저장되었습니다.',
|
||
icon: 'success',
|
||
timer: 1500,
|
||
showConfirmButton: false
|
||
});
|
||
}
|
||
|
||
// 서명 삭제
|
||
function removeSignature(signatureType) {
|
||
Swal.fire({
|
||
title: '확인',
|
||
text: '서명을 지우시겠어요?',
|
||
icon: 'question',
|
||
showCancelButton: true,
|
||
confirmButtonText: '네',
|
||
cancelButtonText: '아니오'
|
||
}).then((result) => {
|
||
if (result.isConfirmed) {
|
||
// hidden input 초기화
|
||
let inputName = '';
|
||
switch(signatureType) {
|
||
case 'contractManager':
|
||
inputName = 'contract_manager_signature';
|
||
break;
|
||
case 'constructionPM':
|
||
inputName = 'construction_pm_signature';
|
||
break;
|
||
case 'departmentHead':
|
||
inputName = 'department_head_signature';
|
||
break;
|
||
case 'ceo':
|
||
inputName = 'ceo_signature';
|
||
break;
|
||
}
|
||
|
||
const hiddenInput = document.querySelector(`input[name="${inputName}"]`);
|
||
if (hiddenInput) {
|
||
hiddenInput.value = '';
|
||
}
|
||
|
||
// 날짜 입력 필드 초기화
|
||
let dateFieldName = '';
|
||
switch(signatureType) {
|
||
case 'contractManager':
|
||
dateFieldName = 'contract_manager_date';
|
||
break;
|
||
case 'constructionPM':
|
||
dateFieldName = 'construction_pm_date';
|
||
break;
|
||
case 'departmentHead':
|
||
dateFieldName = 'department_head_date';
|
||
break;
|
||
case 'ceo':
|
||
dateFieldName = 'ceo_date';
|
||
break;
|
||
}
|
||
|
||
const dateInput = document.querySelector(`input[name="${dateFieldName}"]`);
|
||
if (dateInput) {
|
||
dateInput.value = '';
|
||
}
|
||
|
||
// 서명 이미지 제거
|
||
const signatureDiv = document.getElementById(`${signatureType}Signature`);
|
||
if (signatureDiv) {
|
||
signatureDiv.innerHTML = '';
|
||
}
|
||
|
||
Swal.fire({
|
||
title: '완료',
|
||
text: '서명이 삭제되었습니다.',
|
||
icon: 'success',
|
||
timer: 1500,
|
||
showConfirmButton: false
|
||
});
|
||
}
|
||
});
|
||
}
|
||
|
||
// 계약 ITEM 동적 테이블 관련 전역 변수
|
||
let contractItemData = [];
|
||
|
||
// 모든 input 요소에 autocomplete='off' 속성 적용 (jQuery 사용)
|
||
$(document).ready(function() {
|
||
$('input').attr('autocomplete', 'off');
|
||
|
||
// 기존 계약 ITEM 데이터 로드
|
||
loadContractItems();
|
||
|
||
// 기존 회의 참석자 데이터 로드
|
||
loadAttendees();
|
||
|
||
// textarea 자동 크기 조정 기능 적용
|
||
autoResizeTextareas();
|
||
|
||
// 모드에 따른 입력 요소 상태 설정
|
||
setModeState();
|
||
});
|
||
|
||
// 모드에 따른 입력 요소 상태 설정
|
||
function setModeState() {
|
||
const mode = '<?php echo $mode; ?>';
|
||
const isViewMode = mode === 'view';
|
||
|
||
if (isViewMode) {
|
||
// 조회모드: 모든 입력 요소 비활성화
|
||
$('input, textarea, select').prop('disabled', true).prop('readonly', true);
|
||
|
||
// 서명 버튼 클릭 이벤트 제거 및 숨김
|
||
$('.signature-btn').off('click').addClass('disabled').hide();
|
||
$('.signature-remove').off('click').addClass('disabled').hide();
|
||
|
||
// 동적 버튼들의 클릭 이벤트 제거 및 숨김
|
||
$('.dynamic-button').off('click').addClass('disabled').hide();
|
||
|
||
// 테이블 내 모든 버튼 숨김
|
||
$('.main-table button, .dynamic-table button').hide();
|
||
$('button[type="button"]').hide();
|
||
$('button[onclick*="signature"]').hide();
|
||
$('button[onclick*="remove"]').hide();
|
||
$('button[onclick*="clear"]').hide();
|
||
$('button[onclick*="add"]').hide();
|
||
|
||
console.log('조회모드: 모든 입력 요소와 버튼이 비활성화되었습니다.');
|
||
} else {
|
||
// 수정/신규모드: 모든 입력 요소 활성화
|
||
$('input, textarea, select').prop('disabled', false).prop('readonly', false);
|
||
|
||
// hidden input은 readonly 제거
|
||
$('input[type="hidden"]').prop('readonly', false);
|
||
|
||
console.log('편집모드: 모든 입력 요소가 활성화되었습니다.');
|
||
}
|
||
}
|
||
|
||
// textarea 자동 크기 조정 함수
|
||
function autoResizeTextareas() {
|
||
// 기존 textarea에 자동 크기 조정 적용
|
||
$('textarea').each(function() {
|
||
autoResizeTextarea(this);
|
||
});
|
||
|
||
// 동적으로 생성되는 textarea에도 적용하기 위한 이벤트 델리게이션
|
||
$(document).on('input propertychange paste keyup', 'textarea', function() {
|
||
autoResizeTextarea(this);
|
||
});
|
||
|
||
// 페이지 로드 후 잠시 뒤 다시 한번 크기 조정 (폰트 로딩 완료 후)
|
||
setTimeout(function() {
|
||
$('textarea').each(function() {
|
||
autoResizeTextarea(this);
|
||
});
|
||
}, 500);
|
||
}
|
||
|
||
// 개별 textarea 자동 크기 조정 함수
|
||
function autoResizeTextarea(textarea) {
|
||
// 최소 높이 설정
|
||
const minHeight = 40;
|
||
|
||
// 현재 높이를 최소값으로 리셋
|
||
textarea.style.height = minHeight + 'px';
|
||
|
||
// 스크롤 높이가 더 크면 그에 맞춰 조정
|
||
if (textarea.scrollHeight > minHeight) {
|
||
textarea.style.height = textarea.scrollHeight + 'px';
|
||
}
|
||
}
|
||
|
||
// 계약 ITEM 데이터 로드
|
||
function loadContractItems() {
|
||
<?php if ($handover_data && $handover_data['contract_items']): ?>
|
||
try {
|
||
var contractItems = JSON.parse('<?php echo addslashes($handover_data['contract_items']); ?>');
|
||
if (contractItems && contractItems.length > 0) {
|
||
contractItems.forEach(function(item) {
|
||
addContractItem(item);
|
||
});
|
||
} else {
|
||
// 기본 행 추가
|
||
addContractItem();
|
||
}
|
||
} catch (e) {
|
||
console.error('계약 ITEM 데이터 파싱 오류:', e);
|
||
addContractItem(); // 기본 행 추가
|
||
}
|
||
<?php else: ?>
|
||
// 기본 행 몇 개 추가
|
||
addContractItem({item_name: '철재방화셔터', quantity: '', note: ''});
|
||
addContractItem({item_name: '스크린방화셔터', quantity: '', note: ''});
|
||
<?php endif; ?>
|
||
}
|
||
|
||
// 회의 참석자 데이터 로드
|
||
function loadAttendees() {
|
||
<?php if ($handover_data && $handover_data['meeting_attendees']): ?>
|
||
try {
|
||
var attendees = JSON.parse('<?php echo addslashes($handover_data['meeting_attendees']); ?>');
|
||
if (attendees && attendees.length > 0) {
|
||
attendees.forEach(function(attendee) {
|
||
addAttendee(attendee);
|
||
});
|
||
} else {
|
||
// 기본 참석자 추가
|
||
addAttendee();
|
||
}
|
||
} catch (e) {
|
||
console.error('회의 참석자 데이터 파싱 오류:', e);
|
||
addAttendee(); // 기본 행 추가
|
||
}
|
||
<?php else: ?>
|
||
// 기본 참석자 추가
|
||
addAttendee({name: '신승표', signature: '', unfulfilled_reason: ''});
|
||
addAttendee({name: '유민수', signature: '', unfulfilled_reason: ''});
|
||
addAttendee({name: '계도건', signature: '', unfulfilled_reason: ''});
|
||
addAttendee({name: '윤희채', signature: '', unfulfilled_reason: ''});
|
||
<?php endif; ?>
|
||
}
|
||
|
||
// 계약 ITEM 행 추가
|
||
function addContractItem(data = null) {
|
||
// 조회모드에서는 사용자가 직접 행 추가 불가 (데이터 로드시에는 허용)
|
||
const mode = '<?php echo $mode; ?>';
|
||
if (mode === 'view' && !data) {
|
||
return false;
|
||
}
|
||
|
||
var rowData = data || { item_name: '', quantity: '', note: '' };
|
||
var newRow = $('<tr>');
|
||
|
||
// 구분 컬럼
|
||
newRow.append('<td ><input type="text" name="contract_item_name[]" class="form-control-borderless" value="' + (rowData.item_name || '') + '" placeholder="구분 입력"></td>');
|
||
|
||
// 수량 컬럼
|
||
newRow.append('<td ><input type="text" name="contract_item_quantity[]" class="form-control-borderless" value="' + (rowData.quantity || '') + '" placeholder="수량 입력"></td>');
|
||
|
||
// 비고 컬럼 (삭제 버튼 포함)
|
||
newRow.append('<td style="position: relative;">\
|
||
<input type="text" name="contract_item_note[]" class="form-control-borderless" value="' + (rowData.note || '') + '" placeholder="비고 입력" style="padding-right: 30px;">\
|
||
<button type="button" class="btn btn-sm btn-danger dynamic-button" onclick="removeContractItem(this)" style="position: absolute; right: 5px; top: 50%; transform: translateY(-50%); padding: 2px 6px; font-size: 11px;">×</button>\
|
||
</td>');
|
||
|
||
$('#contractItemTableBody').append(newRow);
|
||
|
||
// 새로 추가된 요소에 모드 설정 적용
|
||
setModeState();
|
||
}
|
||
|
||
// 회의 참석자 행 추가
|
||
function addAttendee(data = null) {
|
||
// 조회모드에서는 사용자가 직접 행 추가 불가 (데이터 로드시에는 허용)
|
||
const mode = '<?php echo $mode; ?>';
|
||
if (mode === 'view' && !data) {
|
||
return false;
|
||
}
|
||
|
||
var rowData = data || { name: '', function: '', signature: '', unfulfilled_reason: '' };
|
||
var attendeeIndex = $('#attendeeTableBody tr').length;
|
||
var newRow = $('<tr>');
|
||
|
||
// 성명 컬럼
|
||
newRow.append('<td><input type="text" name="attendee_name[]" class="form-control-borderless" value="' + (rowData.name || '') + '" placeholder="성명 입력"></td>');
|
||
|
||
// 기능 컬럼 (서명 버튼과 삭제 버튼)
|
||
newRow.append('<td class="attendee-function-column" style="text-align: center;">\
|
||
<button type="button" class="btn btn-sm btn-primary dynamic-button" onclick="openAttendeeSignatureModal(' + attendeeIndex + ')">서명</button>\
|
||
<button type="button" class="btn btn-sm btn-outline-danger mt-1 dynamic-button" onclick="removeAttendee(this)" style="padding: 2px 6px; font-size: 11px;">×</button>\
|
||
</td>');
|
||
|
||
// 서명 컬럼 (서명 이미지 표시)
|
||
var signatureHtml = '';
|
||
if (rowData.signature) {
|
||
signatureHtml = '<img src="data:image/png;base64,' + rowData.signature + '" style="max-width: 100%; max-height: 40px;">\
|
||
<button type="button" class="btn btn-sm btn-danger dynamic-button" onclick="clearAttendeeSignature(' + attendeeIndex + ')" style="position: absolute; top: 2px; right: 2px; padding: 1px 4px; font-size: 10px;">×</button>';
|
||
}
|
||
newRow.append('<td style="text-align: center; position: relative;">\
|
||
<div id="attendeeSignatureDisplay' + attendeeIndex + '" style="min-height: 40px;">' + signatureHtml + '</div>\
|
||
<input type="hidden" name="attendee_signature[]" id="attendeeSignatureInput' + attendeeIndex + '" value="' + (rowData.signature || '') + '">\
|
||
</td>');
|
||
|
||
// 미이행 사유 컬럼
|
||
newRow.append('<td>\
|
||
<input type="text" name="attendee_unfulfilled[]" class="form-control-borderless" value="' + (rowData.unfulfilled_reason || '') + '" placeholder="미이행 사유">\
|
||
</td>');
|
||
|
||
$('#attendeeTableBody').append(newRow);
|
||
|
||
// 새로 추가된 요소에 모드 설정 적용
|
||
setModeState();
|
||
}
|
||
|
||
// 계약 ITEM 행 삭제
|
||
function removeContractItem(button) {
|
||
var row = $(button).closest('tr');
|
||
if ($('#contractItemTableBody tr').length > 1) {
|
||
row.remove();
|
||
} else {
|
||
Swal.fire({
|
||
title: '알림',
|
||
text: '최소 하나의 행은 유지해야 합니다.',
|
||
icon: 'warning',
|
||
confirmButtonText: '확인'
|
||
});
|
||
}
|
||
}
|
||
|
||
// 회의 참석자 행 삭제
|
||
function removeAttendee(button) {
|
||
var row = $(button).closest('tr');
|
||
if ($('#attendeeTableBody tr').length > 1) {
|
||
row.remove();
|
||
} else {
|
||
Swal.fire({
|
||
title: '알림',
|
||
text: '최소 한 명의 참석자는 유지해야 합니다.',
|
||
icon: 'warning',
|
||
confirmButtonText: '확인'
|
||
});
|
||
}
|
||
}
|
||
|
||
// 참석자 서명 모달 열기
|
||
function openAttendeeSignatureModal(index) {
|
||
// 조회모드에서는 서명 모달을 열지 않음
|
||
const mode = '<?php echo $mode; ?>';
|
||
if (mode === 'view') {
|
||
return false;
|
||
}
|
||
|
||
currentSignatureType = 'attendee_' + index;
|
||
$('#signatureModalLabel').text('참석자 서명');
|
||
$('#signatureModal').modal('show');
|
||
|
||
setTimeout(() => {
|
||
initSignatureCanvas();
|
||
if (signatureContext) {
|
||
signatureContext.clearRect(0, 0, signatureCanvas.width, signatureCanvas.height);
|
||
}
|
||
}, 300);
|
||
}
|
||
|
||
// 참석자 서명 삭제
|
||
function clearAttendeeSignature(index) {
|
||
Swal.fire({
|
||
title: '확인',
|
||
text: '서명을 삭제하시겠습니까?',
|
||
icon: 'question',
|
||
showCancelButton: true,
|
||
confirmButtonText: '네',
|
||
cancelButtonText: '아니오'
|
||
}).then((result) => {
|
||
if (result.isConfirmed) {
|
||
// hidden input 초기화
|
||
const hiddenInput = document.getElementById(`attendeeSignatureInput${index}`);
|
||
if (hiddenInput) {
|
||
hiddenInput.value = '';
|
||
}
|
||
|
||
// 서명 이미지 제거
|
||
const signatureDisplay = document.getElementById(`attendeeSignatureDisplay${index}`);
|
||
if (signatureDisplay) {
|
||
signatureDisplay.innerHTML = '';
|
||
}
|
||
|
||
Swal.fire({
|
||
title: '완료',
|
||
text: '서명이 삭제되었습니다.',
|
||
icon: 'success',
|
||
timer: 1500,
|
||
showConfirmButton: false
|
||
});
|
||
}
|
||
});
|
||
}
|
||
|
||
// 계약 ITEM 데이터 수집 및 JSON 변환
|
||
function collectContractItems() {
|
||
var data = [];
|
||
$('#contractItemTableBody tr').each(function() {
|
||
var row = $(this);
|
||
var itemName = row.find('input[name="contract_item_name[]"]').val();
|
||
var quantity = row.find('input[name="contract_item_quantity[]"]').val();
|
||
var note = row.find('input[name="contract_item_note[]"]').val();
|
||
|
||
if (itemName || quantity || note) {
|
||
data.push({
|
||
item_name: itemName,
|
||
quantity: quantity,
|
||
note: note
|
||
});
|
||
}
|
||
});
|
||
$('#contract_items_json').val(JSON.stringify(data));
|
||
return data;
|
||
}
|
||
|
||
// 회의 참석자 데이터 수집
|
||
function collectAttendees() {
|
||
var data = [];
|
||
$('#attendeeTableBody tr').each(function(index) {
|
||
var row = $(this);
|
||
var name = row.find('input[name="attendee_name[]"]').val() || '';
|
||
var signature = $(`#attendeeSignatureInput${index}`).val() || '';
|
||
var unfulfilled = row.find('input[name="attendee_unfulfilled[]"]').val() || '';
|
||
|
||
// 빈 행도 포함하여 순서 유지 (최소한 이름이나 서명이 있는 경우만)
|
||
if (name || signature || unfulfilled) {
|
||
data.push({
|
||
name: name,
|
||
signature: signature, // base64 서명 데이터
|
||
unfulfilled_reason: unfulfilled
|
||
});
|
||
}
|
||
});
|
||
return data;
|
||
}
|
||
|
||
// PDF 모드 준비 함수
|
||
function preparePDFMode() {
|
||
// body에 pdf-mode 클래스 추가
|
||
document.body.classList.add('pdf-mode');
|
||
|
||
// 모든 input과 textarea의 readonly 속성 임시 제거
|
||
$('input, textarea').each(function() {
|
||
const $this = $(this);
|
||
// 현재 readonly 상태를 data 속성에 저장
|
||
if ($this.prop('readonly')) {
|
||
$this.attr('data-was-readonly', 'true');
|
||
$this.prop('readonly', false);
|
||
}
|
||
if ($this.prop('disabled')) {
|
||
$this.attr('data-was-disabled', 'true');
|
||
$this.prop('disabled', false);
|
||
}
|
||
});
|
||
|
||
console.log('PDF 모드 준비 완료: readonly 속성 제거됨');
|
||
}
|
||
|
||
// PDF 모드에서 원래 상태로 복원 함수
|
||
function restoreFromPDFMode() {
|
||
// pdf-mode 클래스 제거
|
||
document.body.classList.remove('pdf-mode');
|
||
|
||
// readonly 속성 복원
|
||
$('input, textarea').each(function() {
|
||
const $this = $(this);
|
||
if ($this.attr('data-was-readonly') === 'true') {
|
||
$this.prop('readonly', true);
|
||
$this.removeAttr('data-was-readonly');
|
||
}
|
||
if ($this.attr('data-was-disabled') === 'true') {
|
||
$this.prop('disabled', true);
|
||
$this.removeAttr('data-was-disabled');
|
||
}
|
||
});
|
||
|
||
// 조회 모드인 경우 다시 모드 상태 적용
|
||
const mode = '<?php echo $mode; ?>';
|
||
if (mode === 'view') {
|
||
setModeState();
|
||
}
|
||
|
||
console.log('PDF 모드에서 복원 완료: readonly 속성 복구됨');
|
||
}
|
||
|
||
</script>
|
||
|
||
</body>
|
||
</html>
|