Files
sam-kd/work/handover_doc.php
hskwon aca1767eb9 초기 커밋: 5130 레거시 시스템
- URL 하드코딩 → .env APP_URL 기반 동적 URL로 변경
- DB 연결 하드코딩 → .env 기반으로 변경
- MySQL strict mode DATE 오류 수정
2025-12-10 20:14:31 +09:00

1674 lines
64 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?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>&nbsp;
</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" > &nbsp; 공사 인수인계 보고서 </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>