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

3618 lines
172 KiB
PHP
Raw 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.php");
exit;
}
include $_SERVER['DOCUMENT_ROOT'] . '/load_header.php';
$title_message = '하단마감재 절곡품';
?>
<link href="../css/bending.css?v=<?php echo time(); ?>" rel="stylesheet"> <!-- 절곡관련 (가이드레일,케이스,하단마감재) -->
<title> <?=$title_message?> </title>
</head>
<body>
<?php
$header = isset($_REQUEST['header']) ? $_REQUEST['header'] : '';
$model_name_search = $_REQUEST['model_name_search'] ?? '';
$firstitem_search = $_REQUEST['firstitem_search'] ?? '';
$model_UA_search = $_REQUEST['model_UA_search'] ?? '';
if ($header === 'header')
require_once($_SERVER['DOCUMENT_ROOT'] . '/myheader.php');
$search = isset($_REQUEST['search']) ? $_REQUEST['search'] : '';
$mode = isset($_REQUEST["mode"]) ? $_REQUEST["mode"] : '';
// 기존 데이터가 있으면 기본 선택할 값
$selectedModel = $_REQUEST['model_name_search'] ?? '' ;
// 대분류 선택값
$selectedMajor = $_REQUEST['major_category'] ?? '' ;
$tablename = 'bottombar';
$today = date("Y-m-d"); // 현재일자 변수지정
$currentDate = date("Y-m-d");
// fromdate 또는 todate가 빈 문자열이거나 null인 경우
if (!isset($fromdate) || $fromdate === "" || $fromdate === null ||
!isset($todate) || $todate === "" || $todate === null) {
$fromdate = date("Y-m-d", strtotime("2024-01-01"));
$todate = $currentDate; // 현재 날짜
$Transtodate = $todate;
} else {
// fromdate와 todate가 모두 설정된 경우
$Transtodate = $todate;
}
$orderby = " ORDER BY registration_date DESC ";
$SettingDate = " registration_date ";
// 기본 조건 (is_deleted가 NULL인 경우)
$conditions = ["is_deleted IS NULL"];
$bindParams = [];
// 날짜 범위 필터 추가
$conditions[] = "$SettingDate BETWEEN :fromdate AND :todate";
$bindParams[":fromdate"] = $fromdate;
$bindParams[":todate"] = $todate;
// firstitem_search 필터 추가
if (!empty($firstitem_search)) {
$conditions[] = "firstitem = :firstitem_search";
$bindParams[":firstitem_search"] = $firstitem_search;
}
// model_name_search 필터 추가
if (!empty($model_name_search)) {
$conditions[] = "model_name = :model_name_search";
$bindParams[":model_name_search"] = $model_name_search;
}
// model_UA_search 필터 추가
if (!empty($model_UA_search)) {
$conditions[] = "model_UA = :model_UA_search";
$bindParams[":model_UA_search"] = $model_UA_search;
}
// 테이블 컬럼 목록을 이용한 검색 조건 추가
if (!empty($search)) {
$columnQuery = $pdo->query("SHOW COLUMNS FROM {$DB}.{$tablename}");
$columns = $columnQuery->fetchAll(PDO::FETCH_COLUMN);
$searchConditions = [];
foreach ($columns as $index => $column) {
// 각 컬럼마다 고유한 파라미터 이름 생성
$paramName = ":search" . $index;
$searchConditions[] = "$column LIKE $paramName";
$bindParams[$paramName] = "%{$search}%";
}
if (!empty($searchConditions)) {
$conditions[] = "(" . implode(" OR ", $searchConditions) . ")";
}
}
// 최종 WHERE 조건 조립
$sqlWhere = implode(" AND ", $conditions);
$sql = "SELECT * FROM {$DB}.{$tablename} WHERE {$sqlWhere} {$orderby}";
// print $sql;
// print_r($bindParams);
// 실행 및 데이터 가져오기
try {
$stmh = $pdo->prepare($sql);
$stmh->execute($bindParams);
$total_row = $stmh->rowCount();
?>
<form id="board_form" name="board_form" method="post" enctype="multipart/form-data">
<input type="hidden" id="mode" name="mode" value="<?=$mode?>">
<input type="hidden" id="num" name="num">
<input type="hidden" id="tablename" name="tablename" value="<?=$tablename?>">
<input type="hidden" id="header" name="header" value="<?=$header?>">
<!-- 절곡 부품 검색 Modal -->
<div id="bendingSearchModal" class="modal fade" tabindex="-1">
<div class="modal-dialog modal-full modal-dialog-scrollable" >
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">절곡 부품 검색</h5>
<button type="button" class="btn-close" id="closeBendingSearchModal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="row mb-3">
<div class="col-md-12">
<div class="d-flex flex-wrap gap-2 align-items-center mb-3" style="flex-wrap: wrap;">
<!-- 대분류 (스크린/철재) 라디오 버튼 -->
<div class="mx-2">
<div class="btn-group" role="group" aria-label="대분류 선택">
<?php
// 대분류 목록 가져오기
$sqlL1 = "SELECT DISTINCT item_sep FROM {$DB}.bending WHERE item_sep IS NOT NULL AND item_sep <> '' ORDER BY item_sep ASC";
$stmhL1 = $pdo->prepare($sqlL1);
$stmhL1->execute();
$l1_list = $stmhL1->fetchAll(PDO::FETCH_COLUMN);
// 전체 옵션 추가
echo '<input type="radio" class="btn-check" name="modal_searchItem" id="modal_searchItem_all" value="" checked>';
echo '<label class="btn btn-outline-secondary btn-sm" for="modal_searchItem_all">전체</label>';
foreach ($l1_list as $item) {
echo '<input type="radio" class="btn-check" name="modal_searchItem" id="modal_searchItem_' . htmlspecialchars($item, ENT_QUOTES, 'UTF-8') . '" value="' . htmlspecialchars($item, ENT_QUOTES, 'UTF-8') . '">';
echo '<label class="btn btn-outline-primary btn-sm" for="modal_searchItem_' . htmlspecialchars($item, ENT_QUOTES, 'UTF-8') . '">' . htmlspecialchars($item, ENT_QUOTES, 'UTF-8') . '</label>';
}
?>
</div>
</div>
<!-- 인정/비인정 라디오 버튼 -->
<div class="mx-2">
<div class="btn-group" role="group" aria-label="인정/비인정 선택">
<?php
$UA_list = ['인정', '비인정'];
// 전체 옵션 추가
echo '<input type="radio" class="btn-check" name="modal_searchUA" id="modal_searchUA_all" value="" checked>';
echo '<label class="btn btn-outline-secondary btn-sm" for="modal_searchUA_all">전체</label>';
foreach ($UA_list as $item) {
echo '<input type="radio" class="btn-check " name="modal_searchUA" id="modal_searchUA_' . htmlspecialchars($item, ENT_QUOTES, 'UTF-8') . '" value="' . htmlspecialchars($item, ENT_QUOTES, 'UTF-8') . '">';
echo '<label class="btn btn-outline-success btn-sm" for="modal_searchUA_' . htmlspecialchars($item, ENT_QUOTES, 'UTF-8') . '">' . htmlspecialchars($item, ENT_QUOTES, 'UTF-8') . '</label>';
}
?>
</div>
</div>
<!-- 중분류 (절곡물 분류) -->
<select id="searchBendingCategory" name="searchBendingCategory" class="form-select" style="width: auto; font-size: 0.7rem;">
<option value="">(중분류)</option>
<?php
// 중분류 목록 가져오기
$sqlL3 = "SELECT DISTINCT item_bending FROM {$DB}.bending WHERE item_bending IS NOT NULL AND is_deleted IS NULL AND item_bending <> '' ORDER BY item_bending ASC";
$stmhL3 = $pdo->prepare($sqlL3);
$stmhL3->execute();
$l3_list = $stmhL3->fetchAll(PDO::FETCH_COLUMN);
foreach($l3_list as $itemVal): ?>
<option value="<?= htmlspecialchars($itemVal, ENT_QUOTES, 'UTF-8') ?>" <?= ($itemVal === '하단마감재') ? 'selected' : '' ?>>
<?= htmlspecialchars($itemVal, ENT_QUOTES, 'UTF-8') ?>
</option>
<?php endforeach; ?>
</select>
<!-- 품명 -->
<select id="searchName" name="searchName" class="form-select" style="width: auto; min-width: 120px; font-size: 0.7rem;">
<option value="">(품명)</option>
<?php
$sqlItem = "SELECT DISTINCT itemName FROM {$DB}.bending WHERE itemName IS NOT NULL AND is_deleted IS NULL AND itemName <> '' ORDER BY itemName ASC";
$stmhItem = $pdo->prepare($sqlItem);
$stmhItem->execute();
$myItemList = $stmhItem->fetchAll(PDO::FETCH_COLUMN);
foreach($myItemList as $itemVal): ?>
<option value="<?= htmlspecialchars($itemVal, ENT_QUOTES, 'UTF-8') ?>">
<?= htmlspecialchars($itemVal, ENT_QUOTES, 'UTF-8') ?>
</option>
<?php endforeach; ?>
</select>
<!-- 검색어 입력 -->
<div class="input-group" style="width: auto; min-width: 180px;">
<input type="text" class="form-control text-start" id="bendingSearchInput" placeholder="추가 검색어 입력" style="font-size: 0.7rem; height:30px!important;" autocomplete="off">
<button class="btn btn-primary" type="button" id="bendingSearchBtn" style="font-size: 0.7rem;"><i class="bi bi-search"></i> 검색</button>
</div>
<!-- 검색 조건 초기화 버튼 -->
<button class="btn btn-outline-secondary" type="button" id="resetSearchBtn" style="font-size: 0.7rem;" ><i class="bi bi-arrow-clockwise"></i> 초기화</button>
<!-- 절곡바라시 생성 -->
<button class="btn btn-success" type="button" id="makeBendingBtn" style="font-size: 0.7rem;" ><i class="bi bi-magic"></i> 절곡바라시 생성</button>
</div>
</div>
</div>
<div class="table-responsive">
<table class="table table-hover table-bordered">
<thead class="table-secondary">
<tr>
<th style="width: 50px;" class="text-center">
<input class="form-check-input" type="checkbox" id="selectAllBendingItems">
</th>
<th style="width: 60px;" class="text-center sortable-header" data-sort="order" data-direction="none">순서 <i class="bi bi-arrow-down-up sort-icon"></i></th>
<th class="text-center sortable-header" data-sort="date" data-direction="none">등록일 <i class="bi bi-arrow-down-up sort-icon"></i></th>
<th class="text-center sortable-header" data-sort="category" data-direction="none">대분류 <i class="bi bi-arrow-down-up sort-icon"></i></th>
<th class="text-center sortable-header" data-sort="ua" data-direction="none">인정/비인정 <i class="bi bi-arrow-down-up sort-icon"></i></th>
<th class="text-center sortable-header" data-sort="subcategory" data-direction="none">중분류 <i class="bi bi-arrow-down-up sort-icon"></i></th>
<th class="text-center sortable-header" data-sort="name" data-direction="none">품명 <i class="bi bi-arrow-down-up sort-icon"></i></th>
<th class="text-center sortable-header" data-sort="spec" data-direction="none">규격 <i class="bi bi-arrow-down-up sort-icon"></i></th>
<th class="text-center sortable-header" data-sort="material" data-direction="none">재질 <i class="bi bi-arrow-down-up sort-icon"></i></th>
<th class="text-center">이미지</th>
<th class="text-center sortable-header" data-sort="width" data-direction="none">폭합(mm) <i class="bi bi-arrow-down-up sort-icon"></i></th>
</tr>
</thead>
<tbody id="bendingSearchResults">
</tbody>
</table>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" id="cancelBendingSearchModal">취소</button>
<button type="button" class="btn btn-success" id="applyBendingSelectionBtn">선택 적용</button>
</div>
</div>
</div>
</div>
<!-- 이미지 확대 모달 -->
<div id="imageModal" class="image-modal">
<div class="image-modal-content">
<img id="modalImage" src="" alt="확대된 이미지">
</div>
</div>
<!-- 이미지 팝업 오버레이 (search_bottombar.php용) -->
<div id="imagePopupOverlay" class="image-popup-overlay">
<div class="image-popup-content">
<img id="popupImage" src="" alt="확대된 이미지">
</div>
</div>
<!-- Modal -->
<div id="myModal" class="modal" tabindex="-1" style="z-index: 99;" data-bs-backdrop="static" data-bs-keyboard="false">
<div class="modal-content modal-fullscreen" >
<div class="modal-header">
<span class="modal-title"><?=$title_message?></span>
<span class="close closeBtn">&times;</span>
</div>
<div class="modal-body">
<div class="modal_detail">
</div>
</div>
</div>
</div>
<div class="container-fluid">
<!-- 전개도 Modal -->
<div id="ModalFlat" class="modal">
<div class="modal-content" style="width:900px; height:750px;">
<div class="modal-header">
<span class="modal-title"><?=$title_message?></span>
<span class="close closeBtn">&times;</span>
</div>
<div class="modal-body d-flex justify-content-center">
<div class="ModalFlat_detail" style="width:1350px;">
</div>
</div>
</div>
</div>
</div>
<div class="container-fluid">
<div class="card justify-content-center text-center mt-1 mb-5">
<div class="card-header">
<div class="d-flex justify-content-center align-items-center">
<span class="text-center fs-5"> <?=$title_message?> </span>
<button type="button" class="btn btn-dark btn-sm mx-3" onclick='location.reload();' title="새로고침"> <i class="bi bi-arrow-clockwise"></i> </button>
<?php if(!empty($header)) : ?>
<button type="button" class="btn btn-dark btn-sm mx-1" onclick="window.location.href='../bendingfee/list.php?header=header'" title="절곡 BOM"> <i class="bi bi-boxes"></i> </button>
<?php endif; ?>
<?php if(empty($header)) : ?>
<button type="button" class="btn btn-dark btn-sm mx-2" onclick='window.close();' > &times; 닫기 </button>
<?php endif; ?>
</div>
</div>
<div class="card-body">
<div class="d-flex justify-content-center align-items-center text-center mt-2 mb-5">
▷ <?= $total_row ?> &nbsp;
<!-- 대분류 라디오 버튼 -->
<div class="mx-2">
<div class="btn-group" role="group" aria-label="대분류 선택">
<?php
$item_list = ['스크린', '철재'];
$selected_item = isset($_REQUEST['firstitem_search']) ? $_REQUEST['firstitem_search'] : '';
// 전체 옵션 추가
$checked_all = (empty($selected_item)) ? ' checked' : '';
echo '<input type="radio" class="btn-check" name="firstitem_search" id="firstitem_all" value=""' . $checked_all . '>';
echo '<label class="btn btn-outline-secondary btn-sm" for="firstitem_all">전체</label>';
foreach ($item_list as $item) {
$checked = ($selected_item === $item) ? ' checked' : '';
echo '<input type="radio" class="btn-check" name="firstitem_search" id="firstitem_' . htmlspecialchars($item, ENT_QUOTES, 'UTF-8') . '" value="' . htmlspecialchars($item, ENT_QUOTES, 'UTF-8') . '"' . $checked . '>';
echo '<label class="btn btn-outline-primary btn-sm" for="firstitem_' . htmlspecialchars($item, ENT_QUOTES, 'UTF-8') . '">' . htmlspecialchars($item, ENT_QUOTES, 'UTF-8') . '</label>';
}
?>
</div>
</div>
<!-- 인정/비인정 라디오 버튼 -->
<div class="mx-2">
<div class="btn-group" role="group" aria-label="인정/비인정 선택">
<?php
$UA_list = ['인정', '비인정'];
$selected_UA = isset($_REQUEST['model_UA_search']) ? $_REQUEST['model_UA_search'] : '';
// 전체 옵션 추가
$checked_all_UA = (empty($selected_UA)) ? ' checked' : '';
echo '<input type="radio" class="btn-check" name="model_UA_search" id="model_UA_all" value=""' . $checked_all_UA . '>';
echo '<label class="btn btn-outline-secondary btn-sm" for="model_UA_all">전체</label>';
foreach ($UA_list as $item) {
$checked = ($selected_UA === $item) ? ' checked' : '';
echo '<input type="radio" class="btn-check" name="model_UA_search" id="model_UA_' . htmlspecialchars($item, ENT_QUOTES, 'UTF-8') . '" value="' . htmlspecialchars($item, ENT_QUOTES, 'UTF-8') . '"' . $checked . '>';
echo '<label class="btn btn-outline-success btn-sm" for="model_UA_' . htmlspecialchars($item, ENT_QUOTES, 'UTF-8') . '">' . htmlspecialchars($item, ENT_QUOTES, 'UTF-8') . '</label>';
}
?>
</div>
</div>
<!-- 제품모델(KSS01 등) 선택 -->
<?php selectModel('model_name_search', $model_name_search); ?>
<div class="inputWrap30">
<input type="text" id="search" class="form-control text-start" style="width:150px; height:30px!important;" name="search" value="<?=$search?>" autocomplete="off" onKeyPress="if (event.keyCode==13){ enter(); }">
<button class="btnClear"></button>
</div>
&nbsp;&nbsp;
<button class="btn btn-outline-dark btn-sm" type="button" id="searchBtn"> <i class="bi bi-search"></i> </button> &nbsp;&nbsp;&nbsp;&nbsp;
<button id="newBtn" type="button" class="btn btn-dark btn-sm me-2"> <i class="bi bi-pencil-square"></i> 신규 </button>
<button id="registImageBtn" type="button" class="btn btn-dark btn-sm mx-1"> <i class="bi bi-pencil-square"></i> 이미지 등록 </button>
</div>
<div class="d-flex justify-content-center text-center mt-5 mb-2">
<div class="table-responsive mt-3 mb-5">
<table class="table table-hover table-bordered" id="myTable">
<thead class="table-primary">
<th class="text-center sortable-header" data-sort="num" data-direction="none" style="width:70px; cursor: pointer;" > 번호 <i class="bi bi-arrow-down-up sort-icon"></i></th>
<th class="text-center sortable-header" data-sort="date" data-direction="none" style="width:120px; cursor: pointer;" > 등록일 <i class="bi bi-arrow-down-up sort-icon"></i></th>
<th class="text-center sortable-header" data-sort="category" data-direction="none" style="width:100px; cursor: pointer;" > 대분류 <i class="bi bi-arrow-down-up sort-icon"></i></th>
<th class="text-center sortable-header" data-sort="ua" data-direction="none" style="width:100px; cursor: pointer;" > 인정/비인정 <i class="bi bi-arrow-down-up sort-icon"></i></th>
<th class="text-center sortable-header" data-sort="subcategory" data-direction="none" style="width:150px; cursor: pointer;" > 제품코드 <i class="bi bi-arrow-down-up sort-icon"></i></th>
<th class="text-center sortable-header" data-sort="size" data-direction="none" style="width:150px; cursor: pointer;" > 가로(폭) X 세로(높이) <i class="bi bi-arrow-down-up sort-icon"></i></th>
<th class="text-center sortable-header" data-sort="keyword" data-direction="none" style="width:200px; cursor: pointer;" > 품목검색어 <i class="bi bi-arrow-down-up sort-icon"></i></th>
<th class="text-center sortable-header" data-sort="finishing" data-direction="none" style="width:150px; cursor: pointer;" > 마감형태 <i class="bi bi-arrow-down-up sort-icon"></i></th>
<th class="text-center sortable-header" data-sort="width" data-direction="none" style="width:120px; cursor: pointer;" > 소요자재량 <i class="bi bi-arrow-down-up sort-icon"></i></th>
<th class="text-center" style="width:100px;" > <i class="bi bi-image"></i> 형태 </th>
<th class="text-center" style="width:100px;" > 작업지시서 </th>
<th class="text-center" style="width:100px;" > 작성 </th>
<th class="text-center" style="width:300px;" > 비고 </th>
</thead>
<tbody>
<?php
$start_num = $total_row;
while($row = $stmh->fetch(PDO::FETCH_ASSOC)) {
include '_row.php';
// DB에서 가져온 값으로 기본값 설정
$selected_model_name = isset($row['model_name']) ? $row['model_name'] : 'KSS01';
$finishing_type = isset($row['finishing_type']) ? $row['finishing_type'] : '';
// 이미지 URL 변수 초기화
$imgUrl = '';
// JSON 파일 경로 (필요에 따라 경로 수정)
$jsonFile = $_SERVER['DOCUMENT_ROOT'].'/bottombar/bottombar.json';
if(file_exists($jsonFile)) {
$jsonData = file_get_contents($jsonFile);
$bottombarImages = json_decode($jsonData, true);
if(is_array($bottombarImages)){
foreach($bottombarImages as $item){
// 모델명과 마감형태가 모두 일치하는 경우
if($item['model_name'] === $selected_model_name && $item['finishing_type'] === $finishing_type){
// 이미지 태그 생성 (목록에서는 width:120px, height:auto)
$imgUrl = "<img src='{$item['image']}' alt='이미지' style='width:120px;height:auto;'>";
break;
}
}
}
}
?>
<tr >
<td class="text-center"><?= $start_num ?></td>
<td class="text-center" onclick="loadForm('view', '<?=$row['num']?>');"><?= $row['registration_date'] ?></td>
<td class="text-center" onclick="loadForm('view', '<?=$row['num']?>');"><?= $row['firstitem'] ?></td>
<td class="text-center" onclick="loadForm('view', '<?=$row['num']?>');"><?= $row['model_UA'] ?></td>
<td class="text-center" onclick="loadForm('view', '<?=$row['num']?>');"><?= $row['model_name'] ?></td>
<td class="text-center fw-bold text-primary" onclick="loadForm('view', '<?=$row['num']?>');" ><?= $row['bar_width'] ?> X <?= $row['bar_height'] ?> </td>
<td class="text-center text-dark" onclick="loadForm('view', '<?=$row['num']?>');" ><?= $row['search_keyword'] ?> </td>
<td class="text-center text-danger" onclick="loadForm('view', '<?=$row['num']?>');" ><?= $row['finishing_type'] ?> </td>
<td class="text-center" onclick="loadForm('view', '<?=$row['num']?>');">
<?php
// material_summary 데이터 파싱 및 표시
$material_summary_display = '';
if (!empty($row['material_summary'])) {
try {
$material_data = json_decode($row['material_summary'], true);
if (is_array($material_data)) {
$summary_items = [];
foreach ($material_data as $material => $total) {
$summary_items[] = $material . '(' . number_format($total) . ')';
}
$material_summary_display = implode('<br>', $summary_items);
}
} catch (Exception $e) {
$material_summary_display = '데이터 오류';
}
}
echo $material_summary_display ?: '-';
?>
</td>
<td class="text-center" onclick="loadForm('view', '<?=$row['num']?>');"> <?= $imgUrl ?> </td>
<td class="text-center" >
<h6> <span class="badge bg-secondary " onclick="viewWork('<?=$row['num']?>');"> 보기 </span> </h6>
</td>
<td class="text-center" onclick="loadForm('view', '<?=$row['num']?>');"><?= $row['author'] ?></td>
<td class="text-start" onclick="loadForm('view', '<?=$row['num']?>');"><?= $row['remark'] ?></td>
</tr>
<?php
$start_num--;
}
} catch (PDOException $Exception) {
print "오류: ".$Exception->getMessage();
}
?>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</form>
<!-- 다이얼로그(편집기) 마크업: imageEditor.js 호출 시 참조 -->
<dialog id="editorDialog">
<div id="editorHeader">
<h5>이미지 편집기</h5>
<button id="applyBtn" class="btn btn-success btn-sm">적용하기</button>
<button type="button" class="btn-close" aria-label="닫기" id="closeEditorBtn"></button>
</div>
<!-- 툴바 -->
<div id="editorToolbar">
<button id="polyBtn" class="btn btn-outline-primary toolbar-btn" title="폴리라인"><i class="bi bi-vector-pen"></i></button>
<button id="freeBtn" class="btn btn-outline-primary toolbar-btn" title="자유선"><i class="bi bi-brush"></i></button>
<button id="lineBtn" class="btn btn-outline-primary toolbar-btn" title="직선 (L키)"><i class="bi bi-slash-lg"></i></button>
<button id="textBtn" class="btn btn-outline-primary toolbar-btn" title="문자입력"><i class="bi bi-type"></i></button>
<button id="eraserBtn" class="btn btn-outline-warning toolbar-btn" title="지우개"><i class="bi bi-eraser-fill"></i></button>
<button id="selectBtn" class="btn btn-outline-secondary toolbar-btn" title="객체선택"><i class="bi bi-cursor-text"></i></button>
<label class="form-check-label" style="font-size: 0.9em; font-weight: 500; cursor: pointer;">
<label style="font-size: 1.4em; font-weight: 500; cursor: pointer; margin-right:100px;">
<input class="form-check-input" type="checkbox" id="rightAngle" checked style="cursor: pointer; z-index: 10; position: relative;">
직각 고정
</label>
<button id="editBtn" class="btn btn-outline-primary btn-sm me-2" title="수정">수정</button>
<button id="clearBtn" class="btn btn-outline-danger btn-sm ms-auto">전체 지우기</button>
</div>
<!-- 지우개 크기 & 색상 -->
<div id="editorToolbar2" class="d-flex align-items-center px-3 pb-2">
<div class="btn-group" role="group" id="colorPicker">
<button type="button" class="btn color-btn active" data-color="#000000" style="background:#000;"></button>
<button type="button" class="btn color-btn" data-color="#ff0000" style="background:#f00;"></button>
<button type="button" class="btn color-btn" data-color="#0000ff" style="background:#00f;"></button>
<button type="button" class="btn color-btn" data-color="#00aa00" style="background:#0a0;"></button>
<button type="button" class="btn color-btn" data-color="#ff8800" style="background:#f80;"></button>
<button type="button" class="btn color-btn" data-color="#800080" style="background:#808;"></button>
<button type="button" class="btn color-btn" data-color="#888888" style="background:#888;"></button>
</div>
<label class="mb-0 me-2">지우개 크기</label>
<input type="range" class="form-range me-2" id="eraserRange" min="5" max="100" step="1" value="20">
<span id="eraserSizeLabel" class="fw-bold">20</span>px
</div>
<!-- 캔버스 영역 -->
<div id="editorBody">
<canvas id="c" width="370" height="300"></canvas>
</div>
</dialog>
<!-- 미니 합계표 팝업 -->
<div id="miniSummaryPopup" style="display:none; position:fixed; z-index:20000; background:white; border:2px solid #007bff; border-radius:8px; box-shadow:0 4px 20px rgba(0,0,0,0.3); padding:10px; min-width:400px; min-height:200px;"></div>
<!-- 이미지 처리 모듈: imageHandler.js include -->
<script src="js/imageHandler.js?v=<?php echo time(); ?>&nocache=<?php echo uniqid(); ?>"></script>
<!-- drawing tool이 필요한 위치(예: 모달 내)에 컨테이너 추가 -->
<div id="drawingToolContainer" style="display:none;"></div>
<!-- 모듈 스크립트 -->
<script type="module">
console.log('=== 모듈 스크립트 로드 시작 ===');
let openImageEditor;
// 즉시 실행 함수로 감싸서 await 사용
(async function() {
try {
const module = await import('/js/imageEditor.js');
openImageEditor = module.openImageEditor;
console.log('openImageEditor 함수 import 완료:', typeof openImageEditor);
console.log('openImageEditor 함수:', openImageEditor);
// 전역 변수로 설정하여 다른 함수에서 접근 가능하도록 함
window.openImageEditor = openImageEditor;
console.log('openImageEditor를 전역 변수로 설정 완료');
} catch (error) {
console.error('모듈 로드 실패:', error);
return;
}
})();
// 전역 함수로 선언하여 loadForm에서 호출할 수 있도록 함
window.initializeImageEditor = function() {
console.log('=== initializeImageEditor 함수 시작 ===');
console.log('Fabric.js 확인:', typeof fabric);
console.log('fabric 객체:', fabric);
if (typeof fabric === 'undefined') {
console.error('Fabric.js가 로드되지 않았습니다!');
return;
}
const openBtn = document.getElementById('openEditorBtn');
const targetImg = document.getElementById('targetImage');
const imageContainer = document.querySelector('.image-container');
const pasteOverlay = document.querySelector('.paste-overlay');
// null 체크 추가
if (!openBtn || !targetImg || !imageContainer || !pasteOverlay) {
console.warn('이미지 편집기 초기화 실패: 필요한 DOM 요소를 찾을 수 없습니다.');
return;
}
// 붙여넣기 이벤트 처리 함수
function handlePaste(event) {
const items = (event.clipboardData || event.originalEvent.clipboardData).items;
for (let item of items) {
if (item.type.indexOf('image') !== -1) {
const blob = item.getAsFile();
const reader = new FileReader();
reader.onload = function(e) {
targetImg.src = e.target.result;
console.log('이미지가 붙여넣기되었습니다:', e.target.result);
// 붙여넣기 성공 피드백
showPasteFeedback(true);
};
reader.readAsDataURL(blob);
event.preventDefault();
break;
}
}
}
// 붙여넣기 피드백 표시
function showPasteFeedback(success = false) {
if (success) {
pasteOverlay.innerHTML = '<i class="bi bi-check-circle"></i><span>이미지가 붙여넣기되었습니다!</span>';
pasteOverlay.style.background = 'rgba(40, 167, 69, 0.8)';
} else {
pasteOverlay.innerHTML = '<i class="bi bi-clipboard-plus"></i><span>Ctrl+V로 이미지 붙여넣기</span>';
pasteOverlay.style.background = 'rgba(0, 123, 255, 0.8)';
}
pasteOverlay.classList.add('show');
setTimeout(() => {
pasteOverlay.classList.remove('show');
}, 2000);
}
// 기존 이벤트 리스너 제거 (중복 방지)
document.removeEventListener('paste', handlePaste);
imageContainer.removeEventListener('click', imageContainerClickHandler);
imageContainer.removeEventListener('focus', imageContainerFocusHandler);
imageContainer.removeEventListener('blur', imageContainerBlurHandler);
openBtn.removeEventListener('click', openBtnClickHandler);
// 이벤트 핸들러 함수들
function imageContainerClickHandler() {
imageContainer.focus();
}
function imageContainerFocusHandler() {
showPasteFeedback();
}
function imageContainerBlurHandler() {
pasteOverlay.classList.remove('show');
}
function openBtnClickHandler() {
console.log('그리기 버튼이 클릭되었습니다.');
console.log('openImageEditor 함수 타입:', typeof openImageEditor);
console.log('openImageEditor 함수:', openImageEditor);
const src = targetImg.src;
console.log('편집할 이미지 소스:', src);
if (typeof openImageEditor !== 'function') {
console.error('openImageEditor가 함수가 아닙니다!');
console.error('openImageEditor 값:', openImageEditor);
return;
}
console.log('openImageEditor 함수 호출 시도...');
try {
const result = openImageEditor(src);
console.log('openImageEditor 호출 결과:', result);
console.log('result 타입:', typeof result);
if (result && typeof result.then === 'function') {
console.log('Promise 반환됨, then 체인 시작...');
result.then(newUrl => {
// 편집된 이미지를 화면에 반영
console.log('편집 완료, 새로운 이미지 URL:', newUrl);
targetImg.src = newUrl;
}).catch(err => {
// 사용자가 취소했을 때
console.log('편집 취소:', err.message);
console.error('편집 오류 상세:', err);
});
} else {
console.error('openImageEditor가 Promise를 반환하지 않습니다!');
}
} catch (error) {
console.error('openImageEditor 호출 중 오류:', error);
console.error('오류 스택:', error.stack);
}
}
// 그리기 관련 코드
// 붙여넣기 이벤트 리스너 추가
document.addEventListener('paste', handlePaste);
// 이미지 컨테이너 클릭 시 포커스
imageContainer.addEventListener('click', imageContainerClickHandler);
// 이미지 컨테이너에 포커스 가능하도록 설정
imageContainer.tabIndex = 0;
imageContainer.title = '클릭 후 Ctrl+V로 이미지 붙여넣기';
// 포커스 시 오버레이 표시
imageContainer.addEventListener('focus', imageContainerFocusHandler);
// 포커스 해제 시 오버레이 숨김
imageContainer.addEventListener('blur', imageContainerBlurHandler);
// 그리기 버튼 클릭 이벤트 리스너 추가
openBtn.addEventListener('click', openBtnClickHandler);
console.log('이미지 편집기 초기화 완료');
console.log('그리기 버튼 이벤트 리스너 등록됨:', openBtn);
};
// 수정 버튼 클릭 이벤트
$(document).on('click', '#editBtn', function() {
// 현재 표시된 데이터 수집
const currentData = collectCurrentData();
// bending/write_form.php로 연결
const popupWidth = 1200;
const popupHeight = 900;
const left = (window.screen.width - popupWidth) / 2;
const top = (window.screen.height - popupHeight) / 2;
// URL 파라미터 구성
const params = new URLSearchParams({
mode: 'get',
item_sep: currentData.item_sep || '',
model_UA: currentData.model_UA || '',
item_bending: '하단마감재',
itemName: currentData.itemName || '',
item_spec: currentData.item_spec || '',
// 추가 데이터 전달
model_name: currentData.model_name || '',
finishing_type: currentData.finishing_type || '',
rail_length: currentData.rail_length || '',
rail_width: currentData.rail_width || '',
check_type: currentData.check_type || ''
});
window.open('/bending/write_form.php?' + params.toString(), '_blank',
'width=' + popupWidth + ',height=' + popupHeight +
',left=' + left + ',top=' + top +
',scrollbars=yes,status=no,toolbar=no,location=no');
});
// 현재 데이터 수집 함수
function collectCurrentData() {
const data = {};
// 기본 정보 수집
data.item_sep = $('input[name="bottombarSearchItem"]:checked').val() || '';
data.model_UA = $('input[name="bottombarSearchUA"]:checked').val() || '';
data.itemName = $('#searchName').val() || '';
data.item_spec = $('#searchSpec').val() || '';
// 모델 정보 수집 (model_flat.php에서 사용되는 정보)
data.model_name = $('#model_name').val() || 'KSS01';
data.finishing_type = $('#finishing_type').val() || 'EGI마감';
data.bar_width = $('#bar_width').val() || '60';
data.bar_height = $('#bar_height').val() || '30';
return data;
}
</script>
<script>
// bottombar.json 파일의 내용을 JavaScript 배열로 로드
var bottombarImages = <?php
$jsonFile = $_SERVER['DOCUMENT_ROOT'].'/bottombar/bottombar.json';
if(file_exists($jsonFile)){
echo file_get_contents($jsonFile);
} else {
echo '[]';
}
// echo '<pre>';
// print_r($jsonFile);
// echo '</pre>';
?>;
var ajaxRequest_write = null;
var dataTable;
var material_regpageNumber;
$(document).ready(function(){
var loader = document.getElementById('loadingOverlay');
if(loader)
loader.style.display = 'none';
// DataTables 초기 설정
dataTable = $('#myTable').DataTable({
"paging": true,
"ordering": true,
"searching": true,
"pageLength": 50, // 한 페이지에 표시할 항목 수
"lengthMenu": [ 50, 100, 200, 500, 1000, 2000], // 페이지당 표시 항목 선택 메뉴
"language": {
"lengthMenu": "Show _MENU_ entries"
},
"order": [[1, 'desc']] // 첫 번째 열을 기준으로 내림차순 정렬
// "serverSide": true, // 서버 사이드 처리 활성화
// "ajax": {
// "url": "/phonebook/path_to_your_server_script.php", // 서버 측 스크립트 URL
// "type": "POST",
// "data": function (d) {
// // 필요에 따라 추가 데이터를 서버로 전송할 수 있음
// d.search = $('#search').val(); // 검색 값 추가
// }
// }
});
// 페이지 번호 복원 (초기 로드 시)
var savedPageNumber = getCookie('bottombarpageNumber');
if (savedPageNumber) {
dataTable.page(parseInt(savedPageNumber) - 1).draw(false); // 쿠키에 저장된 페이지 번호로 이동
}
// 페이지 변경 이벤트 리스너
dataTable.on('page.dt', function() {
var bottombarpageNumber = dataTable.page.info().page + 1;
setCookie('bottombarpageNumber', bottombarpageNumber, 10); // 쿠키에 현재 페이지 번호 저장
});
// 페이지 길이 셀렉트 박스 변경 이벤트 처리
$('#myTable_length select').on('change', function() {
var selectedValue = $(this).val();
dataTable.page.len(selectedValue).draw(); // 페이지 길이 변경
// 변경 후 현재 페이지 번호 복원
savedPageNumber = getCookie('bottombarpageNumber');
if (savedPageNumber) {
dataTable.page(parseInt(savedPageNumber) - 1).draw(false);
}
});
$("#newBtn").on("click", function() {
loadForm('insert');
});
$("#searchBtn").on("click", function() {
$("#board_form").submit();
});
// 라디오 버튼 변경 시 자동 검색
$("input[name='firstitem_search'], input[name='model_UA_search']").on("change", function() {
console.log("라디오 버튼 변경됨:", $(this).attr('name'), $(this).val());
// form 자동 submit으로 페이지 새로고침
$("#board_form").submit();
});
$("#registImageBtn").on("click", function() {
popupCenter('bottombarlist.php' ,'' , 1500, 900);
});
});
function loadForm(mode, num = null) {
console.log('loadForm 호출됨:', { mode, num });
if (num == null) {
$("#mode").val('insert');
} else {
$("#mode").val(mode);
$("#num").val(num);
}
var tablename= $("#tablename").val();
$('.viewmode').prop('disabled', false); // 버튼 활성화 false는 활성화
$.ajax({
type: "POST",
url: "fetch_modal.php",
data: { mode: mode, num: num , tablename : tablename},
dataType: "html",
success: function(response) {
console.log('모달 내용 로드 완료');
$(".modal_detail").html(response);
$("#myModal").show();
// DOM 요소 확인 및 이벤트 리스너 등록
setTimeout(() => {
const openBtn = document.getElementById('openEditorBtn');
const targetImg = document.getElementById('targetImage');
const imageContainer = document.querySelector('.image-container');
const pasteOverlay = document.querySelector('.paste-overlay');
console.log('DOM 요소 확인:', {
openBtn: !!openBtn,
targetImg: !!targetImg,
imageContainer: !!imageContainer,
pasteOverlay: !!pasteOverlay
});
if (openBtn) {
console.log('그리기 버튼 찾음:', openBtn);
console.log('그리기 버튼 텍스트:', openBtn.textContent);
console.log('그리기 버튼 클래스:', openBtn.className);
console.log('그리기 버튼 스타일:', openBtn.style.display);
// 그리기 버튼에 직접 이벤트 리스너 추가
openBtn.onclick = function() {
console.log('그리기 버튼 클릭됨 (직접 이벤트)');
const globalOpenImageEditor = window.openImageEditor;
console.log('전역 openImageEditor 확인:', typeof globalOpenImageEditor);
if (typeof globalOpenImageEditor !== 'function') {
console.error('전역 openImageEditor가 함수가 아닙니다!');
return;
}
const src = targetImg ? targetImg.src : '';
console.log('편집할 이미지 소스:', src);
try {
const result = globalOpenImageEditor(src);
console.log('openImageEditor 호출 결과:', result);
if (result && typeof result.then === 'function') {
result.then(newUrl => {
console.log('편집 완료, 새로운 이미지 URL:', newUrl);
if (targetImg) targetImg.src = newUrl;
}).catch(err => {
console.log('편집 취소:', err.message);
});
}
} catch (error) {
console.error('openImageEditor 호출 중 오류:', error);
}
};
console.log('그리기 버튼 이벤트 리스너 등록 완료');
} else {
console.warn('그리기 버튼을 찾을 수 없습니다!');
}
if (targetImg) console.log('타겟 이미지 찾음:', targetImg.src);
if (imageContainer) console.log('이미지 컨테이너 찾음');
if (pasteOverlay) console.log('붙여넣기 오버레이 찾음');
// 붙여넣기 기능 초기화
if (targetImg && imageContainer && pasteOverlay) {
console.log('붙여넣기 기능 초기화 시작...');
// 붙여넣기 이벤트 처리 함수
function handlePaste(event) {
const items = (event.clipboardData || event.originalEvent.clipboardData).items;
for (let item of items) {
if (item.type.indexOf('image') !== -1) {
const blob = item.getAsFile();
const reader = new FileReader();
reader.onload = function(e) {
targetImg.src = e.target.result;
console.log('이미지가 붙여넣기되었습니다:', e.target.result);
// 붙여넣기 성공 피드백
showPasteFeedback(true);
};
reader.readAsDataURL(blob);
event.preventDefault();
break;
}
}
}
// 붙여넣기 피드백 표시
function showPasteFeedback(success = false) {
if (success) {
pasteOverlay.innerHTML = '<i class="bi bi-check-circle"></i><span>이미지가 붙여넣기되었습니다!</span>';
pasteOverlay.style.background = 'rgba(40, 167, 69, 0.8)';
} else {
pasteOverlay.innerHTML = '<i class="bi bi-clipboard-plus"></i><span>Ctrl+V로 이미지 붙여넣기</span>';
pasteOverlay.style.background = 'rgba(0, 123, 255, 0.8)';
}
pasteOverlay.classList.add('show');
setTimeout(() => {
pasteOverlay.classList.remove('show');
}, 2000);
}
// 이벤트 핸들러 함수들
function imageContainerClickHandler() {
imageContainer.focus();
}
function imageContainerFocusHandler() {
showPasteFeedback();
}
function imageContainerBlurHandler() {
pasteOverlay.classList.remove('show');
}
// 기존 이벤트 리스너 제거 (중복 방지)
document.removeEventListener('paste', handlePaste);
imageContainer.removeEventListener('click', imageContainerClickHandler);
imageContainer.removeEventListener('focus', imageContainerFocusHandler);
imageContainer.removeEventListener('blur', imageContainerBlurHandler);
// 붙여넣기 이벤트 리스너 추가
document.addEventListener('paste', handlePaste);
// 이미지 컨테이너 클릭 시 포커스
imageContainer.addEventListener('click', imageContainerClickHandler);
// 이미지 컨테이너에 포커스 가능하도록 설정
imageContainer.tabIndex = 0;
imageContainer.title = '클릭 후 Ctrl+V로 이미지 붙여넣기';
// 포커스 시 오버레이 표시
imageContainer.addEventListener('focus', imageContainerFocusHandler);
// 포커스 해제 시 오버레이 숨김
imageContainer.addEventListener('blur', imageContainerBlurHandler);
console.log('붙여넣기 기능 초기화 완료');
} else {
console.warn('붙여넣기 기능 초기화 실패: 필요한 DOM 요소를 찾을 수 없습니다.');
}
}, 100);
// mode에 따라 viewmode 요소들 활성화/비활성화
if(mode === 'view') {
$('.viewmode').prop('disabled', true); // 조회 모드에서는 비활성화
console.log('조회 모드: 모든 입력 필드 비활성화');
} else if(mode === 'modify' || mode === 'insert' || mode === 'copy') {
$('.viewmode').prop('disabled', false); // 수정/입력/복사 모드에서는 활성화
console.log('편집 모드: 모든 입력 필드 활성화');
}
// 1. 닫기 버튼들은 Bootstrap의 닫기 기능만 호출하도록 합니다.
$("#closeBtn, .closeBtn").off("click").on("click", function() {
// 안전한 모달 닫기 방법
try {
// 직접 DOM 조작으로 모달 닫기 (Bootstrap 의존성 제거)
const modalElement = document.getElementById('myModal');
if (modalElement) {
// 모달 숨기기
modalElement.style.display = 'none';
modalElement.classList.remove('show');
// body에서 modal-open 클래스 제거
document.body.classList.remove('modal-open');
// backdrop 제거
const backdrops = document.querySelectorAll('.modal-backdrop');
backdrops.forEach(backdrop => backdrop.remove());
console.log('모달이 직접 DOM 조작으로 닫혔습니다.');
$('.viewmode').prop('disabled', false);
} else {
console.error('myModal 요소를 찾을 수 없습니다.');
}
} catch (error) {
console.error('모달 닫기 오류:', error);
// 최후 수단: jQuery 사용
try {
$('#myModal').hide().removeClass('show');
$('body').removeClass('modal-open');
$('.modal-backdrop').remove();
} catch (jqueryError) {
console.error('jQuery 모달 닫기 오류:', jqueryError);
}
}
});
$("#copyBtn").on("click", function() { // 복사버튼
var num = $(this).data("num"); // 'data-num' 속성 값 가져오기
$("#myModal").hide();
$(".modal_detail").html(''); // 모달 내용을 비움
$('.viewmode').prop('disabled', false); // 다시 활성화
loadForm('copy', num);
});
$("#modifyBtn").on("click", function() { // 수정버튼
var num = $(this).data("num"); // 'data-num' 속성 값 가져오기
$("#myModal").hide();
$(".modal_detail").html(''); // 모달 내용을 비움
$('.viewmode').prop('disabled', false); // 다시 활성화
loadForm('modify', num);
});
$("#viewBtn").on("click", function() {
var num = $(this).data("num"); // 'data-num' 속성 값 가져오기
viewUnfold('view', num); // 가져온 num 값을 함수에 전달
});
// 하장바세트 선택에 따른 동작 구현
const checkImage = $('#checkImage');
const prodModelSelect = $('select[name="selectedModel"]');
// 선택된 값에 따라 이미지 설정
const initialCheckType = $("#selectedModel").val();
// select 요소가 변경될 때 이미지 업데이트
prodModelSelect.on('change', function() {
const selectedFinishingType = $('#finishing_type').val();
updateBottombarImage($(this).val(), selectedFinishingType);
});
// finishing_type 변경 시 이미지 업데이트
$('#finishing_type').on('change', function() {
const selectedModel = $('#selectedModel').val();
updateBottombarImage(selectedModel, $(this).val());
});
// 초기 이미지 설정
updateBottombarImage($("#selectedModel").val(), $('#finishing_type').val());
// bottombar 이미지 업데이트 함수
function updateBottombarImage(model, finishing_type) {
let imagePath = '../img/no_image.png';
console.log('bottombarImages', bottombarImages);
console.log('model', model);
console.log('finishing_type', finishing_type);
// bottombarImages 배열에서 model과 finishing_type에 일치하는 항목 검색
if (Array.isArray(bottombarImages)) {
let found = bottombarImages.find(function(item) {
return item.model_name === model && item.finishing_type === finishing_type;
});
if (found) {
imagePath = found.image;
}
}
// checkImage는 이미지 엘리먼트(jQuery 객체)를 가리킵니다.
$('#checkImage').attr('src', imagePath);
}
// 1. 전역 변수로 조립 부품 데이터 관리
let assemblyParts = [];
let material_summary = {}; // 재질별 폭합 데이터를 저장할 전역 변수
// 2. JSON 문자열을 안전하게 배열로 파싱하는 헬퍼 함수
function parseJsonString(jsonString) {
if (!jsonString || typeof jsonString !== 'string') return [];
try {
const parsed = JSON.parse(jsonString);
return Array.isArray(parsed) ? parsed : [];
} catch (e) {
console.error("JSON 파싱 오류:", jsonString, e);
return [];
}
}
// 3. 기존 데이터 로드 (수정/조회 시)
function loadInitialAssemblyData() {
const initialData = $("#bending_components_data").val();
const initialMaterialSummary = $("#material_summary").val();
console.log('각 파트의 assemblyParts에 들어갈 initialData:', initialData);
if (initialData) {
assemblyParts = parseJsonString(initialData);
renderAssemblyTable(); // 초기 데이터로 테이블 렌더링 (재질별 폭합 테이블도 함께 업데이트)
}
// 기존 재질별 폭합 데이터 복원
if (initialMaterialSummary) {
try {
material_summary = JSON.parse(initialMaterialSummary);
console.log('기존 재질별 폭합 데이터 복원:', material_summary);
} catch (e) {
console.warn('재질별 폭합 데이터 파싱 오류:', e);
material_summary = {};
}
}
}
loadInitialAssemblyData();
// 공통 함수: 현재 페이지의 값들을 가져오기
function getCurrentValues(part) {
const values = {};
// 기본 부품 데이터
values.model_UA = $("input[name='model_UA']:checked").val() || '';
const bar_width = $("input[name='bar_width']").val() || '';
const bar_height = $("input[name='bar_height']").val() || '';
values.item_spec = bar_width && bar_height ? `${bar_width}*${bar_height}` : '';
values.item_bending = '하단마감재'; // 절곡그룹은 가이드레일로 고정
values.material = part.material || '';
values.itemName = part.itemName || '';
// 현재 페이지의 첫 번째 라디오버튼 (스크린, 철재)
const selectedFirstitem = $("input[name='firstitem']:checked").val();
values.item_sep = selectedFirstitem || '';
// 현재 페이지의 검색 키워드 (input)
const currentSearchKeyword = $("#search_keyword").val();
if (currentSearchKeyword) {
values.search_keyword = currentSearchKeyword;
}
return values;
}
// 공통 함수: bending 테이블 검색 및 팝업 열기
function searchBendingAndOpenPopup(part, partIndex, defaultMode = 'write', forceMode = false) {
const currentValues = getCurrentValues(part);
console.clear();
console.log('실시간으로 가져온 검색 값들:', currentValues);
// AJAX로 bending 테이블 검색
$.ajax({
url: 'search_bending.php', // bending테이블의 검색 결과를 가져옴
type: 'POST',
data: {
search_keyword: currentValues.search_keyword,
item_sep: currentValues.item_sep,
model_UA: currentValues.model_UA,
item_spec: currentValues.item_spec,
itemName: currentValues.itemName,
material: currentValues.material,
item_bending: currentValues.item_bending
},
dataType: 'json',
success: function(response) {
console.log('bending 테이블 검색 결과:', response);
// mode 변수 설정
let mode = defaultMode;
let num = '';
if (response.success && response.data.num) {
// forceMode가 true이면 모드를 강제로 유지, false이면 기존 로직 적용
if (!forceMode && defaultMode === 'write') {
mode = 'modify';
}
num = response.data.num;
}
// 팝업 설정
const popupWidth = 1800;
const popupHeight = 700;
const left = (window.screen.width - popupWidth) / 2;
const top = (window.screen.height - popupHeight) / 2;
const bar_width = $("input[name='bar_width']").val() || '';
const bar_height = $("input[name='bar_height']").val() || '';
const item_spec = bar_width && bar_height ? `${bar_width}*${bar_height}` : '';
console.log('partIndex:', partIndex);
console.log('part.imgdata:', part.imgdata);
// URL 파라미터 구성
const params = new URLSearchParams({
mode: mode,
num: num,
inputList: JSON.stringify(part.inputList),
bendingrateList: JSON.stringify(part.bendingrateList),
sumList: JSON.stringify(part.sumList),
colorList: JSON.stringify(part.colorList),
AList: JSON.stringify(part.AList),
item_sep: $("input[name='firstitem']:checked").val() || '',
model_UA: $("input[name='model_UA']:checked").val() || '',
item_bending: '하단마감재',
itemName: part.itemName || '',
item_spec : item_spec ,
material: part.material || '',
imgdata: part.imgdata || '',
// assemblypart의 고유번호 전달
partIndex: partIndex
});
console.log('params:', params.toString());
window.open('/bending/write_form.php?' + params.toString(), '_blank',
'width=' + popupWidth + ',height=' + popupHeight +
',left=' + left + ',top=' + top +
',scrollbars=yes,status=no,toolbar=no,location=no');
},
error: function(xhr, status, error) {
console.error('AJAX 오류:', error);
console.error('상태:', status);
console.error('XHR 상세:', {
responseText: xhr.responseText,
status: xhr.status,
statusText: xhr.statusText
});
alert('검색 중 오류가 발생했습니다. 자세한 내용은 콘솔을 확인해주세요.');
}
});
}
// 각 부품별 수정 버튼 클릭 이벤트 bending 테이블 수정
$(document).off('click', '.edit-part-btn').on('click', '.edit-part-btn', function() {
const partIndex = $(this).data('part-index');
const partNum = $(this).data('part-num');
const part = assemblyParts[partIndex];
if (!part) {
console.log('부품 정보를 찾을 수 없습니다.');
return;
}
searchBendingAndOpenPopup(part, partIndex, 'modify');
});
// 각 부품별 다른이름 저장 버튼 클릭 이벤트 bending 테이블 수정
$(document).off('click', '.new-edit-part-btn').on('click', '.new-edit-part-btn', function() {
const partIndex = $(this).data('part-index');
const partNum = $(this).data('part-num');
const part = assemblyParts[partIndex];
if (!part) {
console.log('부품 정보를 찾을 수 없습니다.');
return;
}
searchBendingAndOpenPopup(part, partIndex, 'write', true);
});
// 4-1. 인정/비인정 모델유형 라디오버튼 변경시
// 인정/비인정 모델유형 라디오버튼 변경 핸들러
function handleModelUAChange() {
if ($("input[name='model_UA']:checked").val().includes('비인정') && $("input[name='firstitem']").val().includes('철재')) {
$("select[name='model_name'][value='스크린비인정']").prop("checked", true);
$("#assemblyModelArea").show();
$("#materialSummaryArea").show(); // 재질별 폭합 테이블도 함께 표시
}
else {
$("select[name='model_name'][value='스크린비인정']").prop("checked", true);
$("#assemblyModelArea").show();
$("#materialSummaryArea").show(); // 재질별 폭합 테이블도 함께 표시
}
}
// 이벤트 리스너 등록
$("input[name='model_UA']").on("change", handleModelUAChange);
// mode가 insert나 modify일 때 최초 실행
if (mode === 'insert' || mode === 'modify') {
handleModelUAChange();
}
// 4. 모델이름에 '비인정'이란 단어가 들어가 있으면 모델유형을 비인정으로 설정
$("#model_name").on("change", function() {
// console.log($("#model_name").val());
if ($("#model_name").val().includes('비인정')) {
$("input[name='model_UA'][value='비인정']").prop("checked", true);
$("#assemblyModelArea").show();
$("#materialSummaryArea").show(); // 재질별 폭합 테이블도 함께 표시
} else {
$("input[name='model_UA'][value='인정']").prop("checked", true);
$("#assemblyModelArea").show();
$("#materialSummaryArea").show(); // 재질별 폭합 테이블도 함께 표시
}
});
// 5. '+ 부품 추가' 버튼 클릭 -> 신규 모달 열기
$("#addPartBtn").on("click", function() {
$('#searchBendingCategory').val('하단마감재');
// 안전한 중첩 모달 표시 방법
try {
const modalElement = document.getElementById('bendingSearchModal');
if (modalElement) {
// 중첩 모달 표시
modalElement.style.display = 'block';
modalElement.classList.add('show');
// backdrop 추가 (중첩 모달용)
const backdrop = document.createElement('div');
backdrop.className = 'modal-backdrop fade show';
backdrop.style.zIndex = '1060';
document.body.appendChild(backdrop);
// 모달이 화면 중앙에 오도록 설정
modalElement.style.zIndex = '1065';
console.log('중첩 모달이 직접 DOM 조작으로 표시되었습니다.');
// 모달이 표시된 후 초기 검색 실행
setTimeout(function() {
searchBendingItems();
}, 100);
} else {
console.error('bendingSearchModal 요소를 찾을 수 없습니다.');
}
} catch (error) {
console.error('중첩 모달 표시 오류:', error);
// 최후 수단: jQuery 사용
try {
$('#bendingSearchModal').show().addClass('show');
$('body').append('<div class="modal-backdrop fade show" style="z-index: 1060;"></div>');
$('#bendingSearchModal').css('z-index', '1065');
setTimeout(function() {
searchBendingItems();
}, 100);
} catch (jqueryError) {
console.error('jQuery 중첩 모달 표시 오류:', jqueryError);
}
}
});
// 하단마감재 검색 버튼 클릭 이벤트
$(document).on('click', '#searchBottombarBtn', function(e) {
e.preventDefault();
// 안전한 중첩 모달 표시 방법
try {
const modalElement = document.getElementById('bottombarSearchModal');
if (modalElement) {
// 중첩 모달 표시
modalElement.style.display = 'block';
modalElement.classList.add('show');
// backdrop 추가 (중첩 모달용)
const backdrop = document.createElement('div');
backdrop.className = 'modal-backdrop fade show';
backdrop.style.zIndex = '1060';
document.body.appendChild(backdrop);
// 모달이 화면 중앙에 오도록 설정
modalElement.style.zIndex = '1065';
console.log('하단마감재 중첩 모달이 직접 DOM 조작으로 표시되었습니다.');
// 모달이 표시된 후 초기 작업 실행
setTimeout(function() {
// 제품모델 목록 로드
loadBottombarModels();
// searchBottombarItems 함수가 존재하는지 확인 후 호출
if (typeof searchBottombarItems === 'function') {
searchBottombarItems();
} else {
console.error('searchBottombarItems 함수를 찾을 수 없습니다.');
}
}, 100);
} else {
console.error('bottombarSearchModal 요소를 찾을 수 없습니다.');
}
} catch (error) {
console.error('하단마감재 중첩 모달 표시 오류:', error);
// 최후 수단: jQuery 사용
try {
$('#bottombarSearchModal').show().addClass('show');
$('#bottombarSearchModal').css('z-index', '1065');
$('body').append('<div class="modal-backdrop fade show" style="z-index: 1060;"></div>');
setTimeout(function() {
loadBottombarModels();
if (typeof searchBottombarItems === 'function') {
searchBottombarItems();
}
}, 100);
} catch (jqueryError) {
console.error('jQuery 하단마감재 중첩 모달 표시 오류:', jqueryError);
}
}
});
// 제품모델 목록을 로드하는 함수
function loadBottombarModels() {
$.ajax({
url: '/bottombar/get_bottombar_models.php',
type: 'GET',
success: function(data) {
const modelSelect = $('#searchBottombarModel');
modelSelect.empty();
modelSelect.append('<option value="">(제품모델)</option>');
if (data.success && data.models) {
data.models.forEach(function(model) {
modelSelect.append(`<option value="${model}">${model}</option>`);
});
}
},
error: function() {
console.error('제품모델 목록 로드 실패');
}
});
}
// 6. 절곡 부품 검색
// 검색 모달에서 체크박스로 선택한 항목들에 순서를 부여하고 관리하는 기능
// - 체크박스를 선택하면 자동으로 순서 번호가 부여됩니다 (1, 2, 3...)
// - 체크를 해제하면 순서 번호가 제거됩니다
// - 선택 적용 시 순서대로 부품이 추가됩니다
function searchBendingItems() {
console.log('=== 검색 함수 시작 ===');
// 이 함수는 이전 답변과 동일하게 유지됩니다.
// (searchbending.php를 호출하여 체크박스가 있는 목록을 만듭니다)
const searchText = $('#bendingSearchInput').val();
const selectedItem = $('input[name="modal_searchItem"]:checked').val(); // 모달창 대분류
const selectedUA = $('input[name="modal_searchUA"]:checked').val(); // 모달창 인정/비인정
const selectedBendingCategory = $('#searchBendingCategory').val(); // 모달창 중분류
const selectedName = $('#searchName').val(); // 모달창 품명
const selectedMaterial = $('#searchMaterial').val(); // 모달창 재질
//디버깅: 검색 조건 로그 출력
console.log('검색 조건:', {
searchText,
selectedItem,
selectedUA,
selectedBendingCategory,
selectedName,
selectedMaterial
});
const searchData = {
selectedName: selectedName,
selectedMaterial: selectedMaterial,
selectedItem: selectedItem,
selectedUA: selectedUA,
selectedBendingCategory: selectedBendingCategory,
search: searchText
};
// 빈 값 제거 (선택적)
Object.keys(searchData).forEach(key => {
if (searchData[key] === '' || searchData[key] === null || searchData[key] === undefined) {
delete searchData[key];
}
});
// console.log('전송할 데이터:', searchData);
//console.log('요청 URL:', "/guiderail/search_guiderail.php");
$.ajax({
url: "search_bending.php",
type: 'GET',
data: searchData,
success: function(htmlResponse) {
console.log('=== 검색 성공 ===');
console.log('검색 결과 HTML 길이:', htmlResponse.length);
console.log('검색 결과 HTML:', htmlResponse.substring(0, 500) + '...');
// PHP 경고 메시지가 포함되어 있는지 확인
if (htmlResponse.includes('Warning:') || htmlResponse.includes('Notice:') || htmlResponse.includes('Error:')) {
console.error('PHP 경고/오류가 응답에 포함되어 있습니다:', htmlResponse);
// 경고 메시지를 제거하고 HTML만 추출
const cleanHtml = htmlResponse.replace(/Warning:.*?in.*?on line \d+/g, '')
.replace(/Notice:.*?in.*?on line \d+/g, '')
.replace(/Error:.*?in.*?on line \d+/g, '');
htmlResponse = cleanHtml;
}
const tableBody = $("#bendingSearchResults");
tableBody.empty();
try {
const rows = $(htmlResponse);
// 클릭 순서 배열 초기화
checkboxClickOrder = [];
rows.each(function() {
const num = $(this).find('.bending-item-checkbox').data('num');
if (num) {
// 체크박스 변경 이벤트 추가
$(this).find('.bending-item-checkbox').on('change', function() {
const $row = $(this).closest('tr');
const rowIndex = $row.index();
if (this.checked) {
// 체크된 경우: 클릭 순서 배열에 추가 (중복 방지)
if (!checkboxClickOrder.includes(rowIndex)) {
checkboxClickOrder.push(rowIndex);
}
} else {
// 체크 해제된 경우: 클릭 순서 배열에서 제거
const removeIndex = checkboxClickOrder.indexOf(rowIndex);
if (removeIndex > -1) {
checkboxClickOrder.splice(removeIndex, 1);
}
}
updateOrderNumbers();
$row.toggleClass('table-active', this.checked);
});
// 행 클릭 이벤트 추가
$(this).css('cursor', 'pointer').on('click', function(e) {
if (e.target.type !== 'checkbox') {
const checkbox = $(this).find('.bending-item-checkbox');
checkbox.prop('checked', !checkbox.prop('checked')).trigger('change');
}
});
tableBody.append(this);
}
});
// 초기 순서 번호 설정
updateOrderNumbers();
console.log('검색 결과 행 수:', rows.length);
console.log('=== 검색 완료 ===');
} catch (parseError) {
console.error('HTML 파싱 오류:', parseError);
console.error('원본 응답:', htmlResponse);
tableBody.html('<tr><td colspan="9" class="text-center text-danger">데이터 파싱 중 오류가 발생했습니다.</td></tr>');
}
},
error: function(xhr, status, error) {
console.error('=== 검색 오류 ===');
console.error('검색 오류:', error);
console.error('상태:', status);
console.error('응답:', xhr.responseText);
console.error('상태 코드:', xhr.status);
$("#bendingSearchResults").html('<tr><td colspan="9" class="text-center text-danger">검색 중 오류가 발생했습니다.</td></tr>');
}
});
}
// 부품 순서 재정렬 함수
function reorderComponents(fromIndex, toIndex) {
if (fromIndex === toIndex) return;
// 배열에서 요소 이동
const movedPart = assemblyParts.splice(fromIndex, 1)[0];
assemblyParts.splice(toIndex, 0, movedPart);
// 순서 번호 재할당
assemblyParts.forEach((part, index) => {
part.orderNumber = index + 1;
});
// 테이블 다시 렌더링 (재질별 폭합 테이블도 함께 업데이트)
renderAssemblyTable();
console.log('부품 순서가 변경되었습니다:', assemblyParts.map(p => ({ name: p.itemName, order: p.orderNumber })));
}
// 순서 초기화 함수
function resetOrder() {
assemblyParts.forEach((part, index) => {
part.orderNumber = index + 1;
});
renderAssemblyTable(); // 이 함수 내에서 재질별 폭합 테이블도 업데이트됨
// 모든 체크박스 해제
$('.component-checkbox').prop('checked', false).trigger('change');
}
// 7. 검색 모달 관련 이벤트 핸들러들
// 이벤트 위임을 사용하여 동적으로 생성되는 요소들에 대해서도 이벤트가 작동하도록 함
$(document).off('click change keypress', '#bendingSearchBtn, #modal_searchItem, #modal_searchUA, #searchBendingCategory, #searchName, #searchMaterial, #bendingSearchInput, #resetSearchBtn, #selectAllBendingItems, #applyBendingSelectionBtn, input[name="modal_searchItem"], input[name="modal_searchUA"]');
$(document).on('click change', '#bendingSearchBtn, #modal_searchItem, #modal_searchUA, #searchBendingCategory, #searchName, #searchMaterial, #bendingSearchInput', function(e) {
e.preventDefault();
console.log('검색 조건 변경됨:', $(this).attr('id'), $(this).val());
searchBendingItems();
});
// 라디오 버튼 클릭 시 동적 검색
$(document).on('change', 'input[name="modal_searchItem"], input[name="modal_searchUA"]', function(e) {
e.preventDefault();
console.log('라디오 버튼 변경됨:', $(this).attr('name'), $(this).val());
searchBendingItems();
});
// 라디오 버튼 클릭 시 즉시 검색 실행 (추가 이벤트 핸들러)
$(document).on('click', 'input[name="modal_searchItem"], input[name="modal_searchUA"]', function(e) {
setTimeout(function() {
console.log('라디오 버튼 클릭됨 - 검색 실행');
searchBendingItems();
}, 100);
});
// 라디오 버튼 라벨 클릭 시에도 검색 실행
$(document).on('click', 'label[for^="modal_searchItem_"], label[for^="modal_searchUA_"]', function(e) {
setTimeout(function() {
console.log('라디오 버튼 라벨 클릭됨 - 검색 실행');
searchBendingItems();
}, 100);
});
$(document).on('keypress', '#bendingSearchInput', function(e) {
if (e.which === 13) {
e.preventDefault();
console.log('검색어 엔터키 입력됨');
searchBendingItems();
}
});
// 검색 버튼에 대한 명시적 이벤트 핸들러 추가
$(document).on('click', '#bendingSearchBtn', function(e) {
e.preventDefault();
console.log('검색 버튼 클릭됨');
searchBendingItems();
});
$(document).on('click', '#resetSearchBtn', function(e) {
e.preventDefault();
$('#bendingSearchModal .form-select, #bendingSearchInput').val('');
$('#searchBendingCategory').val('가이드레일');
// 라디오 버튼 초기화
$('input[name="modal_searchItem"]').prop('checked', false);
$('input[name="modal_searchUA"]').prop('checked', false);
$('#modal_searchItem_all').prop('checked', true);
$('#modal_searchUA_all').prop('checked', true);
console.log('검색 조건 초기화됨');
searchBendingItems();
});
// 절곡바라시 생성 클릭시
$(document).on('click', '#makeBendingBtn', function(e) {
e.preventDefault();
// 중복 클릭 방지를 위한 플래그
if ($(this).data('clicked')) {
return;
}
// 클릭 플래그 설정
$(this).data('clicked', true);
// 새 창으로 절곡바라시 작성 폼 열기
var popupWidth = 1800;
var popupHeight = 800;
var left = (window.screen.width - popupWidth) / 2;
var top = (window.screen.height - popupHeight) / 2;
var mode = 'get';
var item_sep = $('input[name="firstitem"]:checked').val();
var model_UA = $('input[name="model_UA"]:checked').val();
var item_bending = '가이드레일';
var itemName = '';
var item_spec = '';
window.open('/bending/write_form.php?mode=' + mode + '&item_sep=' + item_sep + '&model_UA=' + model_UA + '&item_bending=' + item_bending + '&itemName=' + itemName + '&item_spec=' + item_spec, '_blank',
'width=' + popupWidth + ',height=' + popupHeight +
',left=' + left + ',top=' + top +
',scrollbars=yes,status=no,toolbar=no,location=no');
// 1초 후에 클릭 플래그 초기화
setTimeout(() => {
$(this).data('clicked', false);
}, 1000);
});
$(document).on('change', '#selectAllBendingItems', function() {
const isChecked = $(this).prop('checked');
$('#bendingSearchResults .bending-item-checkbox').prop('checked', isChecked).trigger('change');
console.log('전체 선택 상태 변경:', isChecked);
});
// 8. '선택 적용' 버튼 이벤트
$(document).on('click', '#applyBendingSelectionBtn', function(e) {
e.preventDefault();
// checkboxClickOrder 배열을 사용하여 선택된 순서대로 부품 데이터 수집
const orderedParts = [];
checkboxClickOrder.forEach(function(rowIndex, orderIndex) {
const $row = $('#bendingSearchResults tr').eq(rowIndex);
const $checkbox = $row.find('.bending-item-checkbox');
// 체크박스가 여전히 체크되어 있는지 확인
if ($checkbox.length > 0 && $checkbox.is(':checked')) {
const num = $checkbox.data('num');
if (num) {
orderedParts.push({ orderNumber: orderIndex + 1, num });
}
}
});
if (orderedParts.length === 0) {
alert('적용할 부품을 선택하세요.');
return;
}
console.log('선택된 부품 수:', orderedParts.length);
console.log('orderedParts:', orderedParts);
// 순서대로 부품 정보 가져오기
const fetchPromises = orderedParts.map(item =>
$.ajax({
url: "/bending/fetch_bending_detail.php",
type: "POST",
data: { num: item.num },
dataType: 'json'
})
);
// console.log('순서대로 정렬된 부품:', orderedParts);
Promise.all(fetchPromises).then(results => {
// 현재 assemblyParts 배열의 최대 순서 번호 찾기
const maxOrderNumber = assemblyParts.length > 0
? Math.max(...assemblyParts.map(part => part.orderNumber || 0))
: 0;
results.forEach((partData, index) => {
if (partData && !partData.error) {
// 새로운 순서 번호 할당 (기존 최대값 + 1부터 시작)
partData.orderNumber = maxOrderNumber + index + 1;
// 기본 수량 설정
partData.quantity = 1;
console.log('추가되는 부품 데이터:', partData);
console.log('부품 imgdata:', partData.imgdata);
assemblyParts.push(partData);
}
});
renderAssemblyTable(); // 이 함수 내에서 재질별 폭합 테이블도 업데이트됨
// 안전한 중첩 모달 닫기 방법
try {
const modalElement = document.getElementById('bendingSearchModal');
if (modalElement) {
// 모달 숨기기
modalElement.style.display = 'none';
modalElement.classList.remove('show');
// backdrop 제거 (중첩 모달용)
const backdrops = document.querySelectorAll('.modal-backdrop');
backdrops.forEach(backdrop => {
if (backdrop.style.zIndex === '1060') {
backdrop.remove();
}
});
console.log('중첩 모달이 직접 DOM 조작으로 닫혔습니다.');
} else {
console.error('bendingSearchModal 요소를 찾을 수 없습니다.');
}
} catch (error) {
console.error('중첩 모달 닫기 오류:', error);
// 최후 수단: jQuery 사용
try {
$('#bendingSearchModal').hide().removeClass('show');
$('.modal-backdrop[style*="z-index: 1060"]').remove();
} catch (jqueryError) {
console.error('jQuery 중첩 모달 닫기 오류:', jqueryError);
}
}
alertToast(`${results.length}개 부품이 순서대로 추가되었습니다.`);
}).catch(error => {
console.error('부품 정보 가져오기 오류:', error);
alert("부품 정보를 가져오는 중 오류가 발생했습니다.");
});
});
// =========================================================================
// [핵심 수정] 조립 부품 테이블 렌더링 함수
// =========================================================================
function renderAssemblyTable() {
console.clear();
const container = $("#assemblyTable");
container.empty();
if (assemblyParts.length === 0) {
container.html('<tr><td colspan="2" class="text-center text-muted">추가된 부품이 없습니다.</td></tr>');
// 재질별 폭합 테이블도 비움
updateMaterialSummaryTable([]);
return;
}
// 순서 번호로 정렬
const sortedParts = [...assemblyParts].sort((a, b) => {
const orderA = a.orderNumber || 999;
const orderB = b.orderNumber || 999;
return orderA - orderB;
});
sortedParts.forEach((part, partIndex) => {
// 부품 이미지 찾기 함수 (shutterbox/list.php에서 학습한 방법 적용)
function findImageByConditions(items, conditions) {
for (let item of items) {
let matched = true;
for (let key in conditions) {
if (!item[key] || item[key] !== conditions[key]) {
matched = false;
break;
}
}
if (matched && item.image) {
return item.image;
}
}
return '';
}
// 부품 조건에 따른 이미지 찾기
let partImageUrl = '';
if (window.bendingImages && Array.isArray(window.bendingImages)) {
// 검색 조건 우선순위대로 정렬
const searchCases = [];
// 품목검색어(search_keyword)가 있는 경우는 우선시 하고, 없는 경우 대분류, 인정/비인정, 품명 , 재질 , 대분류 전부 같을때만 이미지 나옴
if (part.search_keyword) {
searchCases.push({
search_keyword: part.search_keyword,
itemName: part.itemName,
material: part.material,
item_sep: part.item_sep,
model_UA: part.model_UA
});
}
else {
if (part.itemName && part.material && part.item_sep && part.model_UA) {
searchCases.push({
item_bending: part.item_bending,
itemName: part.itemName,
material: part.material,
item_sep: part.item_sep,
model_UA: part.model_UA
});
}
}
// 순차 검색 - 첫번째 매칭되는 이미지 사용
for (let searchCase of searchCases) {
partImageUrl = findImageByConditions(window.bendingImages, searchCase);
if (partImageUrl) break;
}
console.log('부품 이미지기 찾기 partIndex:', partIndex, 'partImageUrl:', partImageUrl, 'searchCases:', searchCases);
}
// 기존 imgdata가 있으면 우선 사용
if (part.imgdata) {
partImageUrl = `/bending/img/${part.imgdata}`;
}
const componentWrapper = $(`
<tr class="component-block" data-part-index="${partIndex}">
<td style="width: 200px; vertical-align: top;">
<div class="card">
<div class="card-header d-flex justify-content-between align-items-center p-2">
<div class="d-flex align-items-center">
<input type="checkbox" class="form-check-input component-checkbox me-2" data-part-index="${partIndex}">
${part.orderNumber ? `<span class="badge bg-primary me-2">순서: ${part.orderNumber}</span>` : ''}
${partImageUrl ? `<img src="${partImageUrl}" class="search-zoomable-image" style="width: 60px; height: auto; max-height: 60px; object-fit: contain; margin-right: 8px; border-radius: 4px; border: 1px solid #ddd; cursor: pointer;" alt="${part.itemName}" title="마우스 hover 확대보기">` : ''}
<div class="d-flex flex-column">
<span class="fw-bold">${part.itemName}</span>
<span class="text-muted small">재질: ${part.material}</span>
</div>
<div class="d-flex align-items-center ms-2">
<label class="form-label mb-0 me-1" style="font-size: 0.8rem;">수량:</label>
<input type="number" class="form-control form-control-sm quantity-input viewmode"
data-part-index="${partIndex}"
value="${part.quantity || 1}"
min="1"
style="width: 60px; font-size: 0.8rem;">
</div>
</div>
<div class="d-flex gap-1">
<button type="button" class="btn btn-primary btn-sm edit-part-btn viewmode" data-part-index="${partIndex}" data-part-num="${part.num}"><i class="bi bi-pencil"></i> 원본 수정</button>
<button type="button" class="btn btn-primary btn-sm new-edit-part-btn viewmode" data-part-index="${partIndex}" data-part-num="${part.num}"><i class="bi bi-file-earmark-plus"></i> 다른이름 저장</button>
<button type="button" class="btn btn-danger btn-sm remove-part-btn viewmode"><i class="bi bi-trash"></i> 부품 삭제</button>
</div>
</div>
<div class="card-body p-1">
<table class="table table-sm mb-0 component-inner-table"></table>
</div>
</div>
</td>
</tr>
`);
const innerTable = componentWrapper.find('.component-inner-table');
// 각 부품의 상세 데이터 파싱 - 안전한 파싱 함수 사용
const inputList = safeParseJson(part.inputList);
const bendingrateList = safeParseJson(part.bendingrateList);
const sumList = safeParseJson(part.sumList);
const colorList = safeParseJson(part.colorList).map(checked => checked ? '음영' : '');
const aList = safeParseJson(part.AList).map(checked => checked ? 'A각 ' : '');
const calcAfterList = []; // 새로 계산될 배열
console.log('Parsed data for part', partIndex, ':', {
inputList,
bendingrateList,
sumList,
colorList,
aList
});
// 연신율 계산
let accumulated = 0;
for(let i = 0; i < inputList.length; i++) {
const inp = parseInt(inputList[i]) || 0;
const bend = parseInt(bendingrateList[i]) || 0;
const diff = inp + bend; // 덧셈으로 수정
calcAfterList.push(diff);
accumulated += diff;
}
const rowsData = [
{ label: '번호', type: 'span', data: Array.from({length: inputList.length}, (_, i) => i + 1) , className: 'w40px' },
{ label: '입력', type: 'input', name: 'inputList', data: inputList, className: 'yellowBold w40px' },
{ label: '연신율', type: 'input', name: 'bendingrateList', data: bendingrateList },
{ label: '연신율계산 후', type: 'span', data: calcAfterList , className: 'w40px'},
{ label: '합계', type: 'span', data: sumList, className: 'orangeBlackBold w40px' },
{ label: '음영', type: 'span', name: 'colorList', data: colorList, className: 'w40px' },
{ label: 'A각 표시', type: 'span', name: 'AList', data: aList, className: 'w40px'}
];
// console.log('inputList', inputList);
// console.log('bendingrateList', bendingrateList);
// console.log('sumList', sumList);
// console.log('colorList', colorList);
// console.log('aList', aList);
// console.log('rowsData', rowsData);
rowsData.forEach(rowData => {
const $row = $('<tr>').append(`<td class="lightgray" style="width:120px;">${rowData.label}</td>`);
const $cell = $('<td>').addClass('input-container');
for (let i = 0; i < inputList.length; i++) {
const val = rowData.data[i];
let element;
if (rowData.type === 'input') {
element = $('<input type="text">')
.addClass(`form-control text-center ${rowData.className || ''}`)
.attr({
'data-part-index': partIndex,
'data-col-index': i,
'name': `${rowData.name}[${partIndex}]`,
'readonly': true
})
.val(val);
} else if (rowData.type === 'checkbox') {
element = $('<input type="checkbox">')
.addClass('form-check-input')
.attr({
'data-part-index': partIndex,
'data-col-index': i,
'name': `${rowData.name}[${partIndex}]`,
'readonly': true,
'disabled': true
})
.prop('checked', val === true || val === 'true');
} else {
// span 요소 생성
element = $('<span>')
.addClass(`form-control text-center readonly ${rowData.className || ''}`)
.text(val);
// 음영 행인 경우 조건부 스타일 적용
if (rowData.label === '음영' && val === '음영') {
element.addClass('bg-dark text-white');
}
// A각 표시 행인 경우 조건부 스타일 적용
if (rowData.label === 'A각 표시' && val === 'A각 ') {
element.addClass('bg-primary text-white');
}
}
$cell.append(element);
}
$row.append($cell);
innerTable.append($row);
});
container.append(componentWrapper);
});
// 재질별 폭합 테이블 업데이트
updateMaterialSummaryTable(sortedParts);
attachAssemblyTableEvents();
}
// 재질별 폭합 계산 및 테이블 업데이트 함수
function updateMaterialSummaryTable(parts) {
const materialSummary = {};
// 각 부품의 재질과 합계 마지막 값을 수집
parts.forEach(part => {
const material = part.material || '미지정';
const sumList = safeParseJson(part.sumList);
const quantity = parseInt(part.quantity) || 1; // 수량 기본값 1
// 합계 배열의 마지막 값 (가장 큰 값)을 가져옴
let lastSum = 0;
if (sumList && sumList.length > 0) {
// 숫자로 변환하여 가장 큰 값 찾기
const numericSums = sumList.map(val => parseInt(val) || 0);
lastSum = Math.max(...numericSums);
}
// 수량을 곱해서 계산
const totalSum = lastSum * quantity;
if (materialSummary[material]) {
materialSummary[material] += totalSum;
} else {
materialSummary[material] = totalSum;
}
});
// 전역 변수 material_summary 업데이트 (저장용)
window.material_summary = materialSummary;
material_summary = materialSummary; // 전역 변수도 업데이트
// 테이블 렌더링
const tableBody = $("#materialSummaryTable tbody");
tableBody.empty();
if (Object.keys(materialSummary).length === 0) {
tableBody.html('<tr><td colspan="2" class="text-center text-muted">재질별 폭합 데이터가 없습니다.</td></tr>');
return;
}
// 재질별로 정렬하여 테이블 생성
Object.keys(materialSummary).sort().forEach(material => {
const total = materialSummary[material];
const row = $(`
<tr style='width: 200px;'>
<td class="text-center fw-bold">${material}</td>
<td class="text-center fw-bold text-primary">${total.toFixed(0)}</td>
</tr>
`);
tableBody.append(row);
});
console.log('재질별 폭합 업데이트:', materialSummary);
}
// 안전한 JSON 파싱 함수
function safeParseJson(data) {
if (Array.isArray(data)) {
return data;
}
if (typeof data === 'string') {
try {
const parsed = JSON.parse(data);
return Array.isArray(parsed) ? parsed : [];
} catch (e) {
console.warn('JSON 파싱 실패:', e);
return [];
}
}
return [];
}
// [신규] 조립 부품 테이블 이벤트 핸들러 부착 함수
function attachAssemblyTableEvents() {
// 부품 삭제
$("#assemblyTable .remove-part-btn").off('click').on('click', function() {
const partIndex = $(this).closest('.component-block').data('part-index');
assemblyParts.splice(partIndex, 1);
renderAssemblyTable(); // 이 함수 내에서 재질별 폭합 테이블도 업데이트됨
alertToast('부품이 삭제되었습니다.');
});
// 데이터 변경 시 assemblyParts 배열 실시간 업데이트
$("#assemblyTable input").off('input change').on('input change', function() {
const $el = $(this);
const partIndex = $el.data('part-index');
const colIndex = $el.data('col-index');
const nameAttr = $el.attr('name') || '';
const name = nameAttr.split('[')[0]; // inputList, bendingrateList 등
if(partIndex === undefined || colIndex === undefined || !nameAttr) return;
const part = assemblyParts[partIndex];
if (!part) return;
// 기존 배열 데이터 가져오기
let list = safeParseJson(part[name]);
if (!Array.isArray(list)) {
list = [];
}
// 배열 크기 확장 (필요시)
while (list.length <= colIndex) {
list.push('');
}
// 값 업데이트
list[colIndex] = (this.type === 'checkbox') ? this.checked : $el.val();
// assemblyParts 배열에 직접 저장 (JSON 문자열로 변환하지 않음)
part[name] = list;
console.log(`Updated ${name}[${colIndex}] = ${list[colIndex]} for part ${partIndex}`);
});
// 수량 입력 필드 이벤트 핸들러
$("#assemblyTable .quantity-input").off('input change').on('input change', function() {
const $el = $(this);
const partIndex = $el.data('part-index');
const quantity = parseInt($el.val()) || 1;
if (partIndex === undefined) return;
const part = assemblyParts[partIndex];
if (!part) return;
// 수량 업데이트
part.quantity = quantity;
// 재질별 폭합 테이블 업데이트
updateMaterialSummaryTable(assemblyParts);
console.log(`Updated quantity for part ${partIndex}: ${quantity}`);
});
// 체크박스 기반 순서 변경 기능 초기화
initializeCheckboxOrdering();
// 순서 관련 버튼 이벤트
$("#resetOrderBtn").off('click').on('click', function() {
resetOrder();
alertToast('순서가 초기화되었습니다.');
});
// 순서 위로/아래로 이동 버튼
$("#moveUpBtn").off('click').on('click', function() {
moveSelectedComponentsUp();
});
$("#moveDownBtn").off('click').on('click', function() {
moveSelectedComponentsDown();
});
const mode = $("#mode").val();
if(mode == 'view') {
$('.viewmode').prop('disabled', true); // 버튼 비활성화 false는 활성화
}
}
// 조립 부품 테이블에서 체크박스 기반 순서 변경 기능
function initializeCheckboxOrdering() {
// 체크박스 선택 시 시각적 피드백
$(document).on('change', '.component-checkbox', function() {
const $row = $(this).closest('.component-block');
if (this.checked) {
$row.addClass('selected');
} else {
$row.removeClass('selected');
}
});
// 전체 선택 체크박스
$(document).on('change', '#selectAllComponents', function() {
const isChecked = $(this).prop('checked');
$('.component-checkbox').prop('checked', isChecked).trigger('change');
});
}
// 선택된 부품들을 위로 이동
function moveSelectedComponentsUp() {
const selectedCheckboxes = $('.component-checkbox:checked');
if (selectedCheckboxes.length === 0) {
alertToast('이동할 부품을 선택하세요.');
return;
}
const selectedIndices = selectedCheckboxes.map(function() {
return parseInt($(this).data('part-index'));
}).get().sort((a, b) => a - b);
// 위로 이동 가능한 부품들만 필터링
const movableIndices = selectedIndices.filter(index => index > 0);
if (movableIndices.length === 0) {
alertToast('선택된 부품들이 이미 맨 위에 있습니다.');
return;
}
// 위로 이동 실행
movableIndices.forEach(index => {
if (index > 0) {
reorderComponents(index, index - 1);
}
});
// 체크박스 상태 유지
setTimeout(() => {
selectedIndices.forEach(index => {
$(`.component-checkbox[data-part-index="${index - 1}"]`).prop('checked', true).trigger('change');
});
}, 100);
alertToast(`${movableIndices.length}개 부품이 위로 이동되었습니다.`);
}
// 선택된 부품들을 아래로 이동
function moveSelectedComponentsDown() {
const selectedCheckboxes = $('.component-checkbox:checked');
if (selectedCheckboxes.length === 0) {
alertToast('이동할 부품을 선택하세요.');
return;
}
const selectedIndices = selectedCheckboxes.map(function() {
return parseInt($(this).data('part-index'));
}).get().sort((a, b) => b - a); // 역순으로 정렬
// 아래로 이동 가능한 부품들만 필터링
const movableIndices = selectedIndices.filter(index => index < assemblyParts.length - 1);
if (movableIndices.length === 0) {
alertToast('선택된 부품들이 이미 맨 아래에 있습니다.');
return;
}
// 아래로 이동 실행
movableIndices.forEach(index => {
if (index < assemblyParts.length - 1) {
reorderComponents(index, index + 1);
}
});
// 체크박스 상태 유지
setTimeout(() => {
selectedIndices.forEach(index => {
$(`.component-checkbox[data-part-index="${index + 1}"]`).prop('checked', true).trigger('change');
});
}, 100);
alertToast(`${movableIndices.length}개 부품이 아래로 이동되었습니다.`);
}
// 부품 순서 재정렬 함수
function reorderComponents(fromIndex, toIndex) {
if (fromIndex === toIndex) return;
// 배열에서 요소 이동
const movedPart = assemblyParts.splice(fromIndex, 1)[0];
assemblyParts.splice(toIndex, 0, movedPart);
// 순서 번호 재할당
assemblyParts.forEach((part, index) => {
part.orderNumber = index + 1;
});
// 테이블 다시 렌더링 (재질별 폭합 테이블도 함께 업데이트)
renderAssemblyTable();
console.log('부품 순서가 변경되었습니다:', assemblyParts.map(p => ({ name: p.itemName, order: p.orderNumber })));
}
// 순서 초기화 함수
function resetOrder() {
assemblyParts.forEach((part, index) => {
part.orderNumber = index + 1;
});
renderAssemblyTable(); // 이 함수 내에서 재질별 폭합 테이블도 업데이트됨
// 모든 체크박스 해제
$('.component-checkbox').prop('checked', false).trigger('change');
}
// 10. 조립 부품 테이블의 모든 계산을 수행하고 이벤트를 연결하는 함수
function calculateAllComponents() {
$('.component-block').each(function() {
const partIndex = $(this).data('part-index');
const $componentTable = $(this).find('.component-inner-table');
// 계산 함수
const calculate = () => {
let accumulated = 0;
const inputs = $componentTable.find('input[name="inputList"]');
const bends = $componentTable.find('input[name="bendingrateList"]');
const calcs = $componentTable.find('tr:eq(3) span'); // 연신율 계산 후
const sums = $componentTable.find('tr:eq(4) span'); // 합계
inputs.each(function(i) {
const inpVal = parseInt($(this).val()) || 0;
const bendVal = parseInt(bends.eq(i).val()) || 0;
const diff = inpVal + bendVal; // 덧셈으로 수정
// 해당 부품의 `calcAfterList` span을 찾아 값 업데이트
calcs.eq(i).text(diff.toFixed(0));
accumulated += diff;
// 해당 부품의 `sumList` span을 찾아 값 업데이트
sums.eq(i).text(accumulated.toFixed(0));
// assemblyParts 배열 데이터도 업데이트
const part = assemblyParts[partIndex];
if (part) {
// 배열이 없으면 생성
if (!Array.isArray(part.inputList)) part.inputList = [];
if (!Array.isArray(part.bendingrateList)) part.bendingrateList = [];
if (!Array.isArray(part.sumList)) part.sumList = [];
// 배열 크기 확장
while (part.inputList.length <= i) part.inputList.push('');
while (part.bendingrateList.length <= i) part.bendingrateList.push('');
while (part.sumList.length <= i) part.sumList.push('');
part.inputList[i] = inpVal;
part.bendingrateList[i] = bendVal;
part.sumList[i] = accumulated.toFixed(0);
}
});
};
// 입력/연신율 변경 시 계산 다시 실행
$componentTable.find('input[name="inputList"], input[name="bendingrateList"]').on('input change', calculate);
// 체크박스 변경 시 데이터 업데이트
$componentTable.find('input[type="checkbox"]').on('change', function() {
const colIndex = $(this).data('col-index');
const name = $(this).closest('tr').find('td:first').text(); // 음영 or A각 표시
const part = assemblyParts[partIndex];
if (part) {
if (name.includes('음영')) {
if (!Array.isArray(part.colorList)) part.colorList = [];
while (part.colorList.length <= colIndex) part.colorList.push(false);
part.colorList[colIndex] = this.checked;
} else if (name.includes('A각')) {
if (!Array.isArray(part.AList)) part.AList = [];
while (part.AList.length <= colIndex) part.AList.push(false);
part.AList[colIndex] = this.checked;
}
}
});
// 초기 계산 실행
calculate();
});
}
// 11. 부품 삭제 이벤트 (이벤트 위임)
$("#assemblyTable").on("click", ".remove-part-btn", function() {
const partIndex = $(this).closest('.component-block').data('part-index');
assemblyParts.splice(partIndex, 1);
renderAssemblyTable(); // 테이블 전체를 다시 그림
alertToast('부품이 삭제되었습니다.');
});
// 12. 전체 삭제 버튼 이벤트
$("#removeAllPartBtn").on("click", function() {
if (assemblyParts.length === 0) {
alertToast('삭제할 부품이 없습니다.');
return;
}
Swal.fire({
title: '전체 부품 삭제',
text: `현재 ${assemblyParts.length}개의 부품이 있습니다. 모든 부품을 삭제하시겠습니까?`,
icon: 'warning',
showCancelButton: true,
confirmButtonColor: '#d33',
cancelButtonColor: '#3085d6',
confirmButtonText: '전체 삭제',
cancelButtonText: '취소'
}).then((result) => {
if (result.isConfirmed) {
assemblyParts = []; // 모든 부품 삭제
renderAssemblyTable(); // 이 함수 내에서 재질별 폭합 테이블도 업데이트됨
alertToast('모든 부품이 삭제되었습니다.');
}
});
});
// 13. 부품 정보 업데이트 함수 (팝업에서 호출됨)
window.updateAssemblyPart = function(updatedPartData) {
console.log('부품 정보 업데이트 시작:', updatedPartData);
const partIndex = updatedPartData.partIndex;
// assemblyParts 배열에서 해당 부품 찾기
if (assemblyParts[partIndex]) {
// 기존 부품 정보 업데이트
const part = assemblyParts[partIndex];
// 기본 정보 업데이트
part.itemName = updatedPartData.itemName || part.itemName;
part.material = updatedPartData.material || part.material;
part.imgdata = updatedPartData.imgdata || part.imgdata;
part.num = updatedPartData.num || part.num;
// 배열 데이터 업데이트
if (updatedPartData.inputList) {
part.inputList = updatedPartData.inputList;
}
if (updatedPartData.bendingrateList) {
part.bendingrateList = updatedPartData.bendingrateList;
}
if (updatedPartData.sumList) {
part.sumList = updatedPartData.sumList;
}
if (updatedPartData.colorList) {
part.colorList = updatedPartData.colorList;
}
if (updatedPartData.AList) {
part.AList = updatedPartData.AList;
}
console.log('업데이트된 부품 정보:', part);
// 테이블 다시 렌더링
renderAssemblyTable();
// 성공 메시지
alertToast('부품 정보가 업데이트되었습니다.');
// 팝업 닫기
setTimeout(() => {
if (window.opener) {
window.opener.focus();
}
}, 1000);
} else {
console.error('업데이트할 부품을 찾을 수 없습니다. partIndex:', partIndex);
alertToast('부품 정보 업데이트에 실패했습니다.');
}
};
// 저장버튼 처리
let isSaving = false;
$("#saveBtn").off("click").on("click", function() {
if (isSaving) return;
isSaving = true;
var header = $("#header").val();
// 폼 데이터 준비
// 부품저장
console.clear();
console.log('assemblyParts:', assemblyParts);
console.log('material_summary:', material_summary);
$("#bending_components_data").val(JSON.stringify(assemblyParts));
$("#material_summary").val(JSON.stringify(material_summary));
var formData = $("#board_form").serialize(); // 파일전송은 안된다. serialize의 단점
// return false; //임시 탈출
// 먼저 기본 데이터 저장
$.ajax({
url: "process.php",
type: "post",
data: formData,
dataType: 'json',
success: function(response) {
console.log('기본 데이터 저장 완료:', response);
// 이미지 저장 처리
saveImageToJson(response.num);
},
error: function(jqxhr, status, error) {
console.log('기본 데이터 저장 실패:', jqxhr, status, error);
isSaving = false;
Toastify({
text: "저장 실패",
duration: 2000,
close: true,
gravity: "top",
position: "center",
backgroundColor: "#dc3545",
}).showToast();
}
});
});
// 이미지를 JSON에 저장하는 함수
function saveImageToJson(num) {
const targetImg = document.getElementById('targetImage');
if (!targetImg || !targetImg.src) {
console.log('저장할 이미지가 없습니다.');
completeSave();
return;
}
// 이미지가 편집되었는지 확인 (data URL인 경우)
if (targetImg.src.startsWith('data:')) {
console.log('편집된 이미지 발견, JSON에 저장 중...');
// data URL을 Blob으로 변환
fetch(targetImg.src)
.then(res => res.blob())
.then(blob => {
// 1) 검색 조건 데이터 구성
const payload = {
firstitem: $('input[name="firstitem"]:checked').val() || '',
model_UA: $('input[name="model_UA"]:checked').val() || '',
bar_width: $('#bar_width').val() || '',
bar_height: $('#bar_height').val() || '',
model_name: $('#model_name').val() || '',
finishing_type: $('#finishing_type').val() || '',
search_keyword: $('#search_keyword').val() || ''
};
console.log('저장할 이미지 조건:', payload);
// 2) 동일 조건의 기존 이미지 검색
const existingIndex = bottombarImages.findIndex(item => {
const matchBase =
item.firstitem === payload.firstitem &&
item.model_UA === payload.model_UA &&
item.model_name === payload.model_name &&
item.bar_width === payload.bar_width &&
item.bar_height === payload.bar_height &&
item.finishing_type === payload.finishing_type;
if (!matchBase) return false;
if (payload.search_keyword) {
return item.search_keyword === payload.search_keyword;
}
return true;
});
console.log('기존 이미지 검색 결과:', existingIndex > -1 ? `인덱스 ${existingIndex}에서 발견` : '새 이미지');
// 3) 이미지와 폼 데이터 저장 함수
function saveAll(overwrite = false) {
// 이미지 저장 FormData
const formData = new FormData();
Object.entries(payload).forEach(([k,v]) => formData.append(k, v));
formData.append('upfile', blob, 'edited_image.png');
if (overwrite && existingIndex > -1) {
formData.append('index', existingIndex);
}
// 이미지 업로드
return $.ajax({
url: 'put_bottombar_image.php',
type: 'POST',
data: formData,
processData: false,
contentType: false,
dataType: 'json'
});
}
// 4) 기존 이미지 존재 시 덮어쓰기 확인
if (existingIndex > -1) {
Swal.fire({
title: '이미지 덮어쓰기',
text: '동일한 조건의 이미지가 이미 존재합니다. 덮어쓰시겠습니까?',
icon: 'warning',
showCancelButton: true,
confirmButtonColor: '#3085d6',
cancelButtonColor: '#6c757d',
confirmButtonText: '덮어쓰기',
cancelButtonText: '취소'
}).then((result) => {
if (result.isConfirmed) {
saveAll(true)
.then(imageResponse => {
console.log('이미지 덮어쓰기 완료:', imageResponse);
if (imageResponse.success) {
Toastify({
text: "이미지 덮어쓰기 완료",
duration: 1000,
close: true,
gravity: "top",
position: "center",
backgroundColor: "#28a745",
}).showToast();
}
completeSave();
})
.catch(error => {
console.error('이미지 덮어쓰기 실패:', error);
Toastify({
text: "이미지 덮어쓰기 실패",
duration: 2000,
close: true,
gravity: "top",
position: "center",
backgroundColor: "#ffc107",
}).showToast();
completeSave();
});
} else {
console.log('이미지 덮어쓰기 취소됨');
completeSave();
}
});
} else {
// 5) 새 이미지 저장
saveAll(false)
.then(imageResponse => {
console.log('새 이미지 저장 완료:', imageResponse);
if (imageResponse.success) {
Toastify({
text: "새 이미지 저장 완료",
duration: 1000,
close: true,
gravity: "top",
position: "center",
backgroundColor: "#28a745",
}).showToast();
}
completeSave();
})
.catch(error => {
console.error('새 이미지 저장 실패:', error);
Toastify({
text: "새 이미지 저장 실패",
duration: 2000,
close: true,
gravity: "top",
position: "center",
backgroundColor: "#ffc107",
}).showToast();
completeSave();
});
}
})
.catch(error => {
console.error('이미지 Blob 변환 실패:', error);
Toastify({
text: "이미지 변환 실패",
duration: 2000,
close: true,
gravity: "top",
position: "center",
backgroundColor: "#dc3545",
}).showToast();
completeSave();
});
} else {
console.log('편집되지 않은 이미지입니다.');
completeSave();
}
}
// 저장 완료 처리
function completeSave() {
Toastify({
text: "저장완료",
duration: 1000,
close: true,
gravity: "top",
position: "center",
backgroundColor: "#4fbe87",
}).showToast();
setTimeout(function() {
isSaving = false;
$("#myModal").hide();
$(".modal_detail").html(''); // 모달 내용을 비움
location.reload();
}, 1000);
}
$("#deleteBtn").on("click", function() {
var level = '<?= $_SESSION["level"] ?>';
if (level !== '1') {
Swal.fire({
title: '삭제불가',
text: "관리자만 삭제 가능합니다.",
icon: 'error',
confirmButtonText: '확인'
});
return;
}
Swal.fire({
title: '자료 삭제',
text: "삭제는 신중! 정말 삭제하시겠습니까?",
icon: 'warning',
showCancelButton: true,
confirmButtonColor: '#3085d6',
cancelButtonColor: '#d33',
confirmButtonText: '삭제',
cancelButtonText: '취소'
}).then((result) => {
if (result.isConfirmed) {
$("#mode").val('delete');
var formData = $("#board_form").serialize();
$.ajax({
url: "process.php",
type: "post",
data: formData,
success: function(response) {
Toastify({
text: "파일 삭제완료",
duration: 2000,
close: true,
gravity: "top",
position: "center",
style: {
background: "linear-gradient(to right, #00b09b, #96c93d)"
},
}).showToast();
$("#myModal").hide();
location.reload();
},
error: function(jqxhr, status, error) {
console.log(jqxhr, status, error);
}
});
}
});
});
},
error: function(jqxhr, status, error) {
console.log("AJAX Error: ", status, error);
}
});
}
function enter() {
$("#board_form").submit();
}
function inputNumberFormat(obj) {
obj.value = obj.value.replace(/[^0-9]/g, '');
let value = obj.value.replace(/,/g, '');
obj.value = value.replace(/\B(?=(\d{3})+(?!\d))/g, ',');
}
$(document).on("keypress", "input", function(event) {
return event.keyCode != 13;
});
$(document).ready(function(){
// 방문기록 남김
saveMenuLog('하단마감재 생성');
});
function generatePDF() {
// 이벤트의 기본 동작을 방지 (모달창 닫힘 방지)
event.preventDefault();
var deadline = '<?php echo $today; ?>';
var deadlineDate = new Date(deadline);
var formattedDate = "(" + String(deadlineDate.getFullYear()).slice(-2) + "." + ("0" + (deadlineDate.getMonth() + 1)).slice(-2) + "." + ("0" + deadlineDate.getDate()).slice(-2) + ")";
var result = 'KD_하단마감재_' + formattedDate + '.pdf';
var element = document.getElementById('content-to-print');
var opt = {
margin: [10, 3, 12, 3], // Top, right, bottom, left margins
filename: result,
image: { type: 'jpeg', quality: 0.98 },
html2canvas: { scale: 1 },
jsPDF: { unit: 'mm', format: 'a4', orientation: 'portrait' },
pagebreak: { mode: [''] }
};
html2pdf().from(element).set(opt).save();
}
function viewWork(num) {
// 이벤트의 기본 동작을 방지 (모달창 닫힘 방지)
event.preventDefault();
var tablename = '<?php echo $tablename; ?>';
var url = "view.php?mode=view&num=" + num + "&tablename=" + tablename;
customPopup(url, '절곡 작업지시서', 1200, 850);
}
// 그리기 기능 관련 이벤트 핸들러들
// 모달이 열린 후 그리기 기능 초기화
$(document).on('shown.bs.modal', '#myModal', function() {
console.cleaer();
console.log('모달이 열렸습니다. 그리기 기능 초기화 시작...');
// mode가 view면 그리기 기능 초기화하지 않음
if ($('#mode').val() === 'view') {
console.log('view 모드에서는 그리기 기능 비활성화');
return;
}
// 그리기 기능 초기화
setTimeout(() => {
console.log('initializeImageEditor 함수 호출 시도...');
if (window.initializeImageEditor) {
console.log('initializeImageEditor 함수를 찾았습니다. 초기화 시작...');
window.initializeImageEditor();
} else {
console.warn('initializeImageEditor 함수를 찾을 수 없습니다');
}
}, 100);
});
// 2. 모달이 완전히 닫힌 후 실행될 모든 정리 작업을 여기에 모읍니다.
$(document).on('hidden.bs.modal', '#myModal', function() {
// 비활성화된 요소들 다시 활성화
$('.viewmode').prop('disabled', false);
// 모달 내용 비우기
$(".modal_detail").html('');
});
$(document).on("click", ".registImageBtn", function() {
popupCenter('bottombarlist.php' ,'' , 1200, 900);
});
// 테이블을 다시 그린 직후, 또는 addColumn/addInitialColumn 끝에 호출하세요.
function wrapColumnsAndAttachRemove() {
$('#assemblyTable tbody tr').each(function(rowIdx) {
const $cell = $(this).find('td.input-container');
// 이미 wrap 되어 있지 않은 직접 자식(input, span 등)만 골라서 감싸기
$cell
.contents()
.filter(function() {
return this.nodeType === 1 && !$(this).closest('.col-cell').length;
})
.each(function() {
$(this).wrap('<div class="col-cell"></div>');
});
});
// 첫 번째 행(col-cell)마다 삭제 버튼 붙이기 (한번만)
$('#assemblyTable tbody tr:first .col-cell').each(function(idx) {
if (!$(this).find('.remove-col').length) {
$(this).append('<button type="button" class="remove-col"></button>');
}
});
}
// 하단마감재 검색 함수
function searchBottombarItems() {
console.log('=== 하단마감재 검색 함수 시작 ===');
const searchText = $('#bottombarSearchInput').val();
// 라디오 버튼 값 읽기 (대분류, 인정/비인정)
const selectedItem = $('input[name="bottombarSearchItem"]:checked').val(); // 대분류(스크린/철재)
const selectedUA = $('input[name="bottombarSearchUA"]:checked').val(); // 인정/비인정
const selectedModel = $('#searchBottombarModel').val(); // 제품모델
const selectedFinishing = $('#searchBottombarFinishing').val(); // 마감
// 디버깅: 검색 조건 로그 출력
console.log('하단마감재 검색 조건:', {
searchText,
selectedItem,
selectedUA,
selectedModel,
selectedFinishing
});
const searchData = {
searchItem: selectedItem,
searchUA: selectedUA,
searchBottombarModel: selectedModel,
searchBottombarFinishing: selectedFinishing,
search: searchText
};
// 빈 값 제거
Object.keys(searchData).forEach(key => {
if (searchData[key] === '' || searchData[key] === null || searchData[key] === undefined) {
delete searchData[key];
}
});
console.log('전송할 데이터:', searchData);
console.log('요청 URL:', "/bottombar/search_bottombar_json.php");
$.ajax({
url: "/bottombar/search_bottombar_json.php",
type: 'GET',
data: searchData,
success: function(htmlResponse) {
console.log('=== 하단마감재 검색 성공 ===');
console.log('검색 결과 HTML 길이:', htmlResponse.length);
const tableBody = $("#bottombarSearchResults");
tableBody.empty();
const rows = $(htmlResponse);
rows.each(function() {
const index = $(this).data('index');
if (index) {
// 행 클릭 이벤트 추가
$(this).css('cursor', 'pointer').on('click', function(e) {
// 이미지 클릭은 제외
if ($(e.target).is('img')) {
return;
}
// 선택된 행 스타일 변경
$('#bottombarSearchResults tr').removeClass('selected table-active');
$(this).addClass('selected table-active');
});
tableBody.append(this);
}
});
console.log('하단마감재 검색 결과 행 수:', rows.length);
console.log('=== 하단마감재 검색 완료 ===');
},
error: function(xhr, status, error) {
console.error('=== 하단마감재 검색 오류 ===');
console.error('검색 오류:', error);
console.error('상태:', status);
console.error('응답:', xhr.responseText);
console.error('상태 코드:', xhr.status);
$("#bottombarSearchResults").html('<tr><td colspan="9" class="text-center text-danger">검색 중 오류가 발생했습니다.</td></tr>');
}
});
}
// 하단마감재 검색 모달 이벤트 핸들러들
$(document).ready(function() {
// 하단마감재 검색 조건 변경 이벤트
$(document).on('click change', '#bottombarSearchBtn, input[name="searchItem"], input[name="searchUA"], #searchBottombarModel, #searchBottombarType, #searchBottombarFinishing', function(e) {
e.preventDefault();
console.log('하단마감재 검색 조건 변경됨:', $(this).attr('id'), $(this).val());
searchBottombarItems();
});
// 하단마감재 검색어 엔터키 이벤트
$(document).on('keypress', '#bottombarSearchInput', function(e) {
if (e.which === 13) {
e.preventDefault();
console.log('하단마감재 검색어 엔터키 입력됨');
searchBottombarItems();
}
});
// 하단마감재 검색 조건 초기화 버튼
$(document).on('click', '#resetbottombarSearchBtn', function(e) {
e.preventDefault();
// 라디오 버튼 초기화
$('#bottombarSearchModal input[name="searchItem"]').prop('checked', false);
$('#bottombarSearchModal input[name="searchUA"]').prop('checked', false);
$('#searchItem_all').prop('checked', true);
$('#searchUA_all').prop('checked', true);
// select 박스 초기화
$('#bottombarSearchModal .form-select, #bottombarSearchInput').val('');
console.log('하단마감재 검색 조건 초기화됨');
searchBottombarItems();
});
// 하단마감재 행 클릭 시 바로 적용
$(document).on('click', '#bottombarSearchResults tr', function(e) {
// 이미지 클릭은 제외
if ($(e.target).is('img')) {
return;
}
// 선택된 행 스타일 변경
$('#bottombarSearchResults tr').removeClass('selected table-active');
$(this).addClass('selected table-active');
// 선택된 행의 인덱스 가져오기
const index = $(this).data('index');
console.log('선택된 인덱스:', index);
console.log('bottombarImages 배열:', bottombarImages);
if (index !== undefined && index !== null) {
// 전역 bottombarImages 변수 사용 (이미 정의됨)
if (bottombarImages && Array.isArray(bottombarImages)) {
// data-index는 1부터 시작하므로 0부터 시작하는 배열 인덱스로 변환
const arrayIndex = index - 1;
console.log('배열 인덱스:', arrayIndex);
console.log('bottombarImages 길이:', bottombarImages.length);
if (arrayIndex >= 0 && arrayIndex < bottombarImages.length) {
const bottombarData = bottombarImages[arrayIndex];
console.log('적용할 하단마감재 데이터:', bottombarData);
ImageHandler.applybottombarData(bottombarData);
// 안전한 중첩 모달 닫기 방법
try {
const modalElement = document.getElementById('bottombarSearchModal');
if (modalElement) {
// 모달 숨기기
modalElement.style.display = 'none';
modalElement.classList.remove('show');
// backdrop 제거 (중첩 모달용)
const backdrops = document.querySelectorAll('.modal-backdrop');
backdrops.forEach(backdrop => {
if (backdrop.style.zIndex === '1060') {
backdrop.remove();
}
});
console.log('하단마감재 중첩 모달이 직접 DOM 조작으로 닫혔습니다.');
} else {
console.error('bottombarSearchModal 요소를 찾을 수 없습니다.');
}
} catch (error) {
console.error('하단마감재 중첩 모달 닫기 오류:', error);
// 최후 수단: jQuery 사용
try {
$('#bottombarSearchModal').hide().removeClass('show');
$('.modal-backdrop[style*="z-index: 1060"]').remove();
} catch (jqueryError) {
console.error('jQuery 하단마감재 중첩 모달 닫기 오류:', jqueryError);
}
}
alertToast('하단마감재 정보가 적용되었습니다.');
} else {
alert("하단마감재 정보를 찾을 수 없습니다.");
}
} else {
alert("하단마감재 배열이 유효하지 않습니다.");
}
} else {
alert("인덱스 정보를 찾을 수 없습니다.");
}
});
// 하단마감재 검색 모달이 닫힐 때 초기화
$(document).on('hidden.bs.modal', '#bottombarSearchModal', function() {
console.log('하단마감재 검색 모달이 닫혔습니다. 초기화를 실행합니다.');
// 검색 결과 초기화
$('#bottombarSearchResults').empty();
// 검색 조건 초기화
$('#bottombarSearchModal .form-select, #bottombarSearchInput').val('');
// 라디오 버튼 초기화
$('#bottombarSearchModal input[name="searchItem"]').prop('checked', false);
$('#bottombarSearchModal input[name="searchUA"]').prop('checked', false);
$('#searchItem_all').prop('checked', true);
$('#searchUA_all').prop('checked', true);
$('#bottombarSearchModal input[type="checkbox"]').prop('checked', false);
// 중첩 모달 처리를 위한 배경 초기화
$('.modal-backdrop').each(function() {
if ($(this).css('z-index') > 1050) {
$(this).remove();
}
});
// 모달 스타일 초기화
$('#bottombarSearchModal').removeClass('show').css({
'display': 'none',
'background-color': 'transparent',
'z-index': 'auto'
});
// 부모 모달이 있다면 부모 모달 복원
const parentModal = $('.modal.show').not('#bottombarSearchModal');
if (parentModal.length > 0) {
parentModal.css({
'z-index': '1055',
'background-color': 'rgba(0, 0, 0, 0.5)'
});
// 부모 모달의 backdrop 복원
if ($('.modal-backdrop').length === 0) {
$('body').append('<div class="modal-backdrop fade show" style="z-index: 1050;"></div>');
}
}
// body에서 modal-open 클래스 제거 (중첩 모달이 아닌 경우에만)
if ($('.modal.show').length === 0) {
$('body').removeClass('modal-open');
}
console.log('하단마감재 검색 모달 초기화 완료');
});
// 전개도 모달 버튼 이벤트 핸들러들
$(document).on('click', '.btn-generate', function(){
if (!confirm('현재 전개도 데이터를 DB에 저장하시겠습니까?')) return;
// fetch_flat.php에서 정의된 함수들을 호출하려면 모달이 열린 후에만 가능
if (typeof collectTableData === 'function' && typeof saveDataToServer === 'function') {
const data = collectTableData();
saveDataToServer(data);
} else {
alert('전개도 모달을 먼저 열어주세요.');
}
});
$(document).on('click', '.btn-pdf', function(){
if (typeof generatePDF === 'function') {
generatePDF();
} else {
alert('전개도 모달을 먼저 열어주세요.');
}
});
$(document).on('click', '.btn-work', function(){
const num = $(this).data('num');
if (num) {
viewWork(num);
} else {
alert('작업 번호를 찾을 수 없습니다.');
}
});
});
// 절곡 부품 검색 모달 닫기 버튼 이벤트 핸들러
$(document).on('click', '#closeBendingSearchModal, #cancelBendingSearchModal', function() {
// 안전한 중첩 모달 닫기 방법
try {
const modalElement = document.getElementById('bendingSearchModal');
if (modalElement) {
// 모달 숨기기
modalElement.style.display = 'none';
modalElement.classList.remove('show');
// backdrop 제거 (중첩 모달용)
const backdrops = document.querySelectorAll('.modal-backdrop');
backdrops.forEach(backdrop => {
if (backdrop.style.zIndex === '1060') {
backdrop.remove();
}
});
console.log('절곡 부품 중첩 모달이 직접 DOM 조작으로 닫혔습니다.');
} else {
console.error('bendingSearchModal 요소를 찾을 수 없습니다.');
}
} catch (error) {
console.error('절곡 부품 중첩 모달 닫기 오류:', error);
// 최후 수단: jQuery 사용
try {
$('#bendingSearchModal').hide().removeClass('show');
$('.modal-backdrop[style*="z-index: 1060"]').remove();
} catch (jqueryError) {
console.error('jQuery 절곡 부품 중첩 모달 닫기 오류:', jqueryError);
}
}
});
// 하단마감재 검색 모달 닫기 버튼 이벤트 핸들러
$(document).on('click', '#closebottombarSearchModal, #cancelbottombarSearchModal', function() {
// 안전한 중첩 모달 닫기 방법
try {
const modalElement = document.getElementById('bottombarSearchModal');
if (modalElement) {
// 모달 숨기기
modalElement.style.display = 'none';
modalElement.classList.remove('show');
// backdrop 제거 (중첩 모달용)
const backdrops = document.querySelectorAll('.modal-backdrop');
backdrops.forEach(backdrop => {
if (backdrop.style.zIndex === '1060') {
backdrop.remove();
}
});
console.log('하단마감재 중첩 모달이 직접 DOM 조작으로 닫혔습니다.');
} else {
console.error('bottombarSearchModal 요소를 찾을 수 없습니다.');
}
} catch (error) {
console.error('하단마감재 중첩 모달 닫기 오류:', error);
// 최후 수단: jQuery 사용
try {
$('#bottombarSearchModal').hide().removeClass('show');
$('.modal-backdrop[style*="z-index: 1060"]').remove();
} catch (jqueryError) {
console.error('jQuery 하단마감재 중첩 모달 닫기 오류:', jqueryError);
}
}
});
// 하단마감재 검색 모달용 CSS 스타일 추가
$('<style>')
.prop('type', 'text/css')
.html(`
#bottombarSearchResults tr {
transition: background-color 0.2s ease;
}
#bottombarSearchResults tr:hover {
background-color: #f8f9fa !important;
}
#bottombarSearchResults tr.selected {
background-color: #007bff !important;
color: white !important;
}
#bottombarSearchResults tr.selected:hover {
background-color: #0056b3 !important;
}
`)
.appendTo('head');
// 하단마감재 검색 모달 HTML 추가
$(document).ready(function() {
$('body').append(`
<div id="bottombarSearchModal" class="modal fade" tabindex="-2">
<div class="modal-dialog modal-fullscreen modal-dialog-scrollable">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">하단마감재 조건 및 이미지 검색</h5>
<button type="button" class="btn-close" id="closebottombarSearchModal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="row mb-3">
<div class="col-md-12">
<div class="d-flex flex-wrap gap-2 align-items-center mb-3" style="flex-wrap: wrap;">
<!-- 대분류 (스크린/철재) 라디오 버튼 -->
<div class="btn-group" role="group" aria-label="대분류 선택" style="font-size: 0.9rem;">
<input type="radio" class="btn-check" name="bottombarSearchItem" id="bottombarSearchItem_all" value="" checked>
<label class="btn btn-outline-secondary btn-sm" for="bottombarSearchItem_all">전체</label>
<input type="radio" class="btn-check" name="bottombarSearchItem" id="bottombarSearchItem_스크린" value="스크린">
<label class="btn btn-outline-primary btn-sm" for="bottombarSearchItem_스크린">스크린</label>
<input type="radio" class="btn-check" name="bottombarSearchItem" id="bottombarSearchItem_철재" value="철재">
<label class="btn btn-outline-primary btn-sm" for="bottombarSearchItem_철재">철재</label>
</div>
<!-- 인정/비인정 라디오 버튼 -->
<div class="btn-group" role="group" aria-label="인정/비인정 선택" style="font-size: 0.9rem;">
<input type="radio" class="btn-check" name="bottombarSearchUA" id="bottombarSearchUA_all" value="" checked>
<label class="btn btn-outline-secondary btn-sm" for="bottombarSearchUA_all">전체</label>
<input type="radio" class="btn-check" name="bottombarSearchUA" id="bottombarSearchUA_인정" value="인정">
<label class="btn btn-outline-success btn-sm" for="bottombarSearchUA_인정">인정</label>
<input type="radio" class="btn-check" name="bottombarSearchUA" id="bottombarSearchUA_비인정" value="비인정">
<label class="btn btn-outline-success btn-sm" for="bottombarSearchUA_비인정">비인정</label>
</div>
<!-- 제품모델 -->
<select id="searchBottombarModel" name="searchBottombarModel" class="form-select" style="width: auto; min-width: 120px; font-size: 0.9rem;">
<option value="">(제품모델)</option>
</select>
<!-- 마감 -->
<select id="searchBottombarFinishing" name="searchBottombarFinishing" class="form-select" style="width: auto; min-width: 120px; font-size: 0.9rem;">
<option value="">(마감)</option>
<option value="SUS마감">SUS마감</option>
<option value="EGI마감">EGI마감</option>
</select>
<!-- 검색어 입력 -->
<div class="input-group" style="width: auto; min-width: 200px;">
<input type="text" class="form-control text-start" id="bottombarSearchInput" placeholder="품목검색어 입력" style="font-size: 0.9rem; height:35px!important;" autocomplete="off">
<button class="btn btn-primary" type="button" id="bottombarSearchBtn" style="font-size: 0.9rem;"><i class="bi bi-search"></i> 검색</button>
</div>
<!-- 검색 조건 초기화 버튼 -->
<button class="btn btn-outline-secondary" type="button" id="resetbottombarSearchBtn" style="font-size: 0.9rem;" ><i class="bi bi-arrow-clockwise"></i> 초기화</button>
</div>
</div>
</div>
<div class="table-responsive">
<table class="table table-hover table-bordered">
<thead class="table-secondary">
<tr>
<th>대분류</th>
<th>인정/비인정</th>
<th>제품모델</th>
<th>마감재 너비(폭)</th>
<th>마감재 높이</th>
<th>마감</th>
<th>품목검색어</th>
<th>이미지</th>
</tr>
</thead>
<tbody id="bottombarSearchResults">
<!-- 검색 결과가 여기에 표시됩니다 -->
</tbody>
</table>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" id="cancelbottombarSearchModal">취소</button>
</div>
</div>
</div>
</div>
`);
});
// 라디오 버튼 변경 시 검색
$(document).on('change', 'input[name="bottombarSearchItem"]', function() {
searchBottombarItems();
});
$(document).on('change', 'input[name="bottombarSearchUA"]', function() {
searchBottombarItems();
});
// search_shutterbox.php 이미지 호버 확대 기능 - 마우스 위치에 팝업 표시
let hoverTimeout;
$(document).on('mouseenter', '.search-zoomable-image', function(e) {
e.stopPropagation(); // 행 클릭 이벤트 전파 방지
const imgSrc = $(this).attr('data-src') || $(this).attr('src');
// 기존 타임아웃 클리어
clearTimeout(hoverTimeout);
// 마우스 위치 계산
const mouseX = e.clientX;
const mouseY = e.clientY;
// 팝업 위치 설정 (마우스 위치에서 약간 오프셋)
const popupOffsetX = 20;
const popupOffsetY = 20;
// 팝업이 화면 밖으로 나가지 않도록 조정
const popupWidth = 300; // 팝업 너비
const popupHeight = 300; // 팝업 높이
const windowWidth = $(window).width();
const windowHeight = $(window).height();
let finalX = mouseX + popupOffsetX;
let finalY = mouseY + popupOffsetY;
// 오른쪽 경계 체크
if (finalX + popupWidth > windowWidth) {
finalX = mouseX - popupWidth - popupOffsetX;
}
// 아래쪽 경계 체크
if (finalY + popupHeight > windowHeight) {
finalY = mouseY - popupHeight - popupOffsetY;
}
// 팝업 위치 설정
$('#imagePopupOverlay').css({
'position': 'fixed',
'top': finalY + 'px',
'left': finalX + 'px',
'width': popupWidth + 'px',
'height': popupHeight + 'px',
'background': 'white',
'border': '2px solid #007bff',
'border-radius': '8px',
'box-shadow': '0 4px 20px rgba(0,0,0,0.3)',
'z-index': '10000',
'display': 'none'
});
// 팝업 내용 설정
$('#popupImage').attr('src', imgSrc);
$('#imagePopupOverlay').fadeIn(200);
});
$(document).on('mouseleave', '.search-zoomable-image', function(e) {
// 마우스가 이미지를 벗어날 때 약간의 지연 후 팝업 닫기
hoverTimeout = setTimeout(function() {
$('#imagePopupOverlay').fadeOut(200);
}, 100);
});
// 팝업 오버레이에 마우스가 들어오면 팝업 유지
$('#imagePopupOverlay').on('mouseenter', function() {
clearTimeout(hoverTimeout);
});
// 팝업 오버레이에서 마우스가 벗어나면 팝업 닫기
$('#imagePopupOverlay').on('mouseleave', function() {
$('#imagePopupOverlay').fadeOut(200);
});
// 순서 번호 업데이트 함수 (테이블 정렬 기능용)
// 테이블 정렬 기능
let currentSort = {
column: null,
direction: 'asc'
};
// 체크박스 클릭 순서를 추적하는 변수
let checkboxClickOrder = [];
// 정렬 헤더 클릭 이벤트 (메인 테이블과 모달 내부 테이블 모두 적용)
$(document).on('click', '.sortable-header', function() {
const sortType = $(this).data('sort');
const $table = $(this).closest('table');
const $tbody = $table.find('tbody');
const $rows = $tbody.find('tr').toArray();
// 현재 테이블의 이전 정렬 헤더에서 data-direction 초기화 및 아이콘 리셋
const $currentTable = $(this).closest('table');
$currentTable.find('.sortable-header').attr('data-direction', 'none');
$currentTable.find('.sortable-header .sort-icon').removeClass('bi-arrow-up bi-arrow-down').addClass('bi-arrow-down-up');
// 정렬 방향 결정 및 data-direction 설정
if (currentSort.column === sortType && currentSort.direction === 'asc') {
currentSort.direction = 'desc';
$(this).attr('data-direction', 'desc');
$(this).find('.sort-icon').removeClass('bi-arrow-down-up bi-arrow-up').addClass('bi-arrow-down');
} else {
currentSort.direction = 'asc';
$(this).attr('data-direction', 'asc');
$(this).find('.sort-icon').removeClass('bi-arrow-down-up bi-arrow-down').addClass('bi-arrow-up');
}
currentSort.column = sortType;
// 행 정렬
$rows.sort(function(a, b) {
let aValue, bValue;
switch(sortType) {
case 'order':
// 순서 정렬 (숫자로 변환)
aValue = parseInt($(a).find('td:eq(1)').text().trim()) || 0;
bValue = parseInt($(b).find('td:eq(1)').text().trim()) || 0;
break;
case 'date':
// 등록일 정렬
aValue = $(a).find('td:eq(2)').text().trim();
bValue = $(b).find('td:eq(2)').text().trim();
break;
case 'category':
// 대분류 정렬
aValue = $(a).find('td:eq(3)').text().trim();
bValue = $(b).find('td:eq(3)').text().trim();
break;
case 'ua':
// 인정/비인정 정렬
aValue = $(a).find('td:eq(4)').text().trim();
bValue = $(b).find('td:eq(4)').text().trim();
break;
case 'subcategory':
// 중분류 정렬
aValue = $(a).find('td:eq(5)').text().trim();
bValue = $(b).find('td:eq(5)').text().trim();
break;
case 'name':
// 품명 정렬
aValue = $(a).find('td:eq(6)').text().trim();
bValue = $(b).find('td:eq(6)').text().trim();
break;
case 'spec':
// 규격 정렬
aValue = $(a).find('td:eq(7)').text().trim();
bValue = $(b).find('td:eq(7)').text().trim();
break;
case 'material':
// 재질 정렬
aValue = $(a).find('td:eq(8)').text().trim();
bValue = $(b).find('td:eq(8)').text().trim();
break;
case 'width':
// 폭합 정렬 (숫자로 변환)
aValue = parseFloat($(a).find('td:eq(10)').text().trim()) || 0;
bValue = parseFloat($(b).find('td:eq(10)').text().trim()) || 0;
break;
default:
return 0;
}
// 문자열 비교
if (typeof aValue === 'string' && typeof bValue === 'string') {
aValue = aValue.toLowerCase();
bValue = bValue.toLowerCase();
}
if (currentSort.direction === 'asc') {
return aValue > bValue ? 1 : -1;
} else {
return aValue < bValue ? 1 : -1;
}
});
// 정렬된 행을 테이블에 다시 추가
$tbody.empty();
$rows.forEach(function(row) {
$tbody.append(row);
});
// 정렬 후 클릭 순서 배열 업데이트 (행 인덱스가 변경되었으므로)
const newClickOrder = [];
checkboxClickOrder.forEach(function(oldRowIndex) {
// 정렬 전의 행을 찾아서 정렬 후의 새로운 인덱스로 변환
const $oldRow = $rows[oldRowIndex];
const newIndex = $tbody.find('tr').index($oldRow);
if (newIndex !== -1) {
newClickOrder.push(newIndex);
}
});
checkboxClickOrder = newClickOrder;
// 정렬 후 행 클릭 이벤트 다시 바인딩
$tbody.find('tr').each(function() {
// 체크박스 변경 이벤트 추가
$(this).find('.bending-item-checkbox').off('change').on('change', function() {
const $row = $(this).closest('tr');
const rowIndex = $row.index();
if (this.checked) {
// 체크된 경우: 클릭 순서 배열에 추가 (중복 방지)
if (!checkboxClickOrder.includes(rowIndex)) {
checkboxClickOrder.push(rowIndex);
}
} else {
// 체크 해제된 경우: 클릭 순서 배열에서 제거
const removeIndex = checkboxClickOrder.indexOf(rowIndex);
if (removeIndex > -1) {
checkboxClickOrder.splice(removeIndex, 1);
}
}
updateOrderNumbers();
$row.toggleClass('table-active', this.checked);
});
// 행 클릭 이벤트 추가
$(this).css('cursor', 'pointer').off('click').on('click', function(e) {
if (e.target.type !== 'checkbox') {
const checkbox = $(this).find('.bending-item-checkbox');
checkbox.prop('checked', !checkbox.prop('checked')).trigger('change');
}
});
});
// 순서 번호는 정렬 시 재설정하지 않음 (사용자가 직접 선택한 순서 유지)
});
// 순서 번호 업데이트 함수
function updateOrderNumbers() {
// 모달 내부 테이블의 순서 번호 업데이트
const $modalTable = $('#bendingSearchModal .sortable-header').closest('table');
if ($modalTable.length > 0) {
const $modalRows = $modalTable.find('tbody tr');
// 모든 순서 번호 초기화
$modalRows.each(function() {
const $orderCell = $(this).find('.order-number');
if ($orderCell.length > 0) {
$orderCell.text('-');
}
});
// 클릭 순서에 따라 순서 번호 부여
checkboxClickOrder.forEach(function(rowIndex, orderIndex) {
const $row = $modalRows.eq(rowIndex);
const $checkbox = $row.find('.bending-item-checkbox');
// 체크박스가 여전히 체크되어 있는지 확인
if ($checkbox.length > 0 && $checkbox.is(':checked')) {
const $orderCell = $row.find('.order-number');
if ($orderCell.length > 0) {
$orderCell.text(orderIndex + 1);
}
}
});
}
}
</script>
<?php include $_SERVER['DOCUMENT_ROOT'] . '/common/enlargeImage.php'; // 이미지 확대4배 JS코드 ?>
</body>
</html>