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

3511 lines
173 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters

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

<?php
require_once($_SERVER['DOCUMENT_ROOT'] . "/session.php");
if (!isset($_SESSION["level"]) || $_SESSION["level"] > 5) {
sleep(1);
header("Location:" . $WebSite . "login/login_form.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
$header = $_REQUEST['header'] ?? '';
$model_name_search = $_REQUEST['model_name_search'] ?? '';
$model_UA_search = $_REQUEST['model_UA_search'] ?? '';
$firstitem_search = $_REQUEST['firstitem_search'] ?? '';
$search_keyword_search = $_REQUEST['search_keyword_search'] ?? '';
// 헤더 파일 로드 (필요시)
if ($header === 'header') {
require_once($_SERVER['DOCUMENT_ROOT'] . '/myheader.php');
}
$search = isset($_REQUEST['search']) ? $_REQUEST['search'] : '';
$mode = isset($_REQUEST['mode']) ? $_REQUEST['mode'] : '';
$tablename = 'guiderail';
// DB 연결 (예: $pdo = db_connect(); 등)
require_once($_SERVER['DOCUMENT_ROOT'] . "/lib/mydb.php");
$pdo = db_connect();
// 날짜 관련
$today = date("Y-m-d");
$currentDate = date("Y-m-d");
// fromdate, todate 초기화
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 {
$Transtodate = $todate;
}
// 기본 ORDER BY
$orderby = " ORDER BY registration_date DESC, num DESC ";
$SettingDate = " registration_date ";
// WHERE 조건을 담을 배열
$conditions = [];
$bindParams = [];
// 삭제되지 않은 데이터만
$conditions[] = " (is_deleted IS NULL OR is_deleted = '0' OR is_deleted = '' ) ";
// 날짜 범위 필터
$conditions[] = "$SettingDate BETWEEN :fromdate AND :todate";
$bindParams[':fromdate'] = $fromdate;
$bindParams[':todate'] = $Transtodate;
// 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;
}
// SHOW COLUMNS를 이용해 테이블 컬럼 목록 조회
$columnSql = "SHOW COLUMNS FROM {$DB}.{$tablename}";
$columnQuery = $pdo->query($columnSql);
$columns = $columnQuery->fetchAll(PDO::FETCH_COLUMN);
// 검색어 필터
if (!empty($search)) {
// 공백 제거(원한다면 제거하지 않아도 됨)
$searchTrimmed = str_replace(' ', '', $search);
$searchConditions = [];
foreach ($columns as $i => $col) {
// 컬럼마다 고유 파라미터 이름(:search0, :search1 등)을 생성
$paramName = ":search{$i}";
$searchConditions[] = "$col LIKE $paramName";
$bindParams[$paramName] = "%{$searchTrimmed}%";
}
if (!empty($searchConditions)) {
// 여러 컬럼 중 하나라도 일치하면 OR로 연결
$conditions[] = "(" . implode(" OR ", $searchConditions) . ")";
}
}
// 최종 SQL 구성
$sql = "SELECT * FROM {$DB}.{$tablename}";
if (!empty($conditions)) {
$sql .= " WHERE " . implode(" AND ", $conditions);
}
$sql .= $orderby;
// 디버그용 출력 (필요시)
// echo $sql;
// print_r($bindParams);
try {
$stmt = $pdo->prepare($sql);
// bindValue를 직접 루프로 돌려 1:1 매칭을 명확히 해도 되고, 아래처럼 바로 execute에 배열을 전달해도 됩니다.
$stmt->execute($bindParams);
$total_row = $stmt->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?>">
<input type="hidden" id="author" name="author" value="<?=$_SESSION['name']?>">
<!-- 절곡 부품 검색 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">순서</th>
<th class="sortable-header" data-sort="date" style="cursor: pointer;">
등록일 <i class="sort-icon bi bi-arrow-down-up"></i>
</th>
<th class="sortable-header" data-sort="category" style="cursor: pointer;">
대분류 <i class="sort-icon bi bi-arrow-down-up"></i>
</th>
<th class="sortable-header" data-sort="ua" style="cursor: pointer;">
인정/비인정 <i class="sort-icon bi bi-arrow-down-up"></i>
</th>
<th class="sortable-header" data-sort="subcategory" style="cursor: pointer;">
중분류 <i class="sort-icon bi bi-arrow-down-up"></i>
</th>
<th class="sortable-header" data-sort="name" style="cursor: pointer;">
품명 <i class="sort-icon bi bi-arrow-down-up"></i>
</th>
<th class="sortable-header" data-sort="material" style="cursor: pointer;">
재질 <i class="sort-icon bi bi-arrow-down-up"></i>
</th>
<th>이미지</th>
<th class="sortable-header" data-sort="width" style="cursor: pointer;">
폭합(mm) <i class="sort-icon bi bi-arrow-down-up"></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_guiderail.php용) -->
<div id="imagePopupOverlay" class="image-popup-overlay">
<div class="image-popup-content">
<img id="popupImage" src="" alt="확대된 이미지">
</div>
</div>
<div class="container-fluid">
<!-- 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:1400px; height:830px;">
<div class="modal-header" style="background-color: green; color: white;">
<span class="modal-title"><?=$title_message?></span>
<span class="close FlatcloseBtn">&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 w150px" name="search" style="height:32px!important;" 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 mx-1"> <i class="bi bi-pencil-square"></i> 신규 </button>
<button type="button" class="btn btn-dark btn-sm mx-1 registImageBtn"> <i class="bi bi-pencil-square"></i> 결합형태 이미지 등록 </button>
<button type="button" class="btn btn-dark btn-sm mx-1" id="modelFlatBtn">
<i class="bi bi-diagram-3"></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" id="myTable">
<thead class="table-primary">
<th class="text-center sortable-header" data-sort="num" 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" 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" 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" 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" style="width:100px; cursor: pointer;" > 제품코드 <i class="bi bi-arrow-down-up sort-icon"></i></th>
<th class="text-center sortable-header" data-sort="keyword" style="width:100px; cursor: pointer;" > 품목검색어 <i class="bi bi-arrow-down-up sort-icon"></i></th>
<th class="text-center sortable-header" data-sort="size" 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="shape" style="width:100px; cursor: pointer;" > 형상 <i class="bi bi-arrow-down-up sort-icon"></i></th>
<th class="text-center sortable-header" data-sort="finishing" style="width:120px; cursor: pointer;" > 마감 <i class="bi bi-arrow-down-up sort-icon"></i></th>
<th class="text-center sortable-header" data-sort="width" style="width:120px; cursor: pointer;" > 소요자재량 <i class="bi bi-arrow-down-up sort-icon"></i></th>
<th class="text-center" style="width:80px;" > <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:70px;" > 작성 </th>
<th class="text-center" style="width:300px;" > 비고 </th>
</thead>
<tbody>
<?php
// 이미지 찾기 함수
function findImageByConditions($items, $conditions) {
foreach ($items as $item) {
$matched = true;
foreach ($conditions as $key => $value) {
if (empty($item[$key]) || $item[$key] !== $value) {
$matched = false;
break;
}
}
if ($matched && !empty($item['image'])) {
return "<img src='{$item['image']}' alt='이미지' style='width:50px;height:50px;'>";
}
}
return '';
}
$start_num = $total_row;
while($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
include '_row.php';
// guiderail.json 파일 읽기
$jsonFile = $_SERVER['DOCUMENT_ROOT'].'/guiderail/guiderail.json';
$imgUrl = '';
if (file_exists($jsonFile)) {
$jsonData = file_get_contents($jsonFile);
$guiderailImages = json_decode($jsonData, true);
if (is_array($guiderailImages)) {
// 검색 조건 우선순위대로 정렬
$searchCases = [];
// 1순위: 검색 키워드 (가장 우선)
if (!empty($row['search_keyword'])) {
$searchCases[] = ['search_keyword' => $row['search_keyword']];
}
// 2순위: 모든 조건 일치
$searchCases[] = [
'model_name' => $row['model_name'],
'check_type' => $row['check_type'],
'finishing_type' => $row['finishing_type'],
'firstitem' => $row['firstitem'],
'UA' => $row['model_UA']
];
// 3순위: 모델명 + 형태 + 마감
$searchCases[] = [
'model_name' => $row['model_name'],
'check_type' => $row['check_type'],
'finishing_type' => $row['finishing_type']
];
// 4순위: 모델명 + 형태
$searchCases[] = [
'model_name' => $row['model_name'],
'check_type' => $row['check_type']
];
// 5순위: 모델명만
$searchCases[] = ['model_name' => $row['model_name']];
// 순차 검색 - 첫번째 매칭되는 이미지 사용
foreach ($searchCases as $case) {
$imgUrl = findImageByConditions($guiderailImages, $case);
if ($imgUrl) 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" onclick="loadForm('view', '<?=$row['num']?>');"><?= $row['search_keyword'] ?></td>
<td class="text-center fw-bold text-primary" onclick="loadForm('view', '<?=$row['num']?>');" ><?= $row['rail_length'] ?> X <?= $row['rail_width'] ?> </td>
<td class="text-center text-danger" onclick="loadForm('view', '<?=$row['num']?>');" ><?= $row['check_type'] ?> </td>
<td class="text-center" 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>
<!-- 그리기 도구 모듈: drawingTool.js include -->
<script src="js/drawingTool.js?v=<?php echo time(); ?>&nocache=<?php echo uniqid(); ?>"></script>
<!-- drawing tool이 필요한 위치(예: 모달 내)에 컨테이너 추가 -->
<div id="drawingToolContainer" style="display:none;"></div>
<!-- 모듈 스크립트 -->
<script type="module">
import { openImageEditor } from '/js/imageEditor.js';
// 전역 함수로 선언하여 loadForm에서 호출할 수 있도록 함
window.initializeImageEditor = function() {
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() {
const src = targetImg.src;
openImageEditor(src)
.then(newUrl => {
// 편집된 이미지를 화면에 반영
targetImg.src = newUrl;
})
.catch(err => {
// 사용자가 취소했을 때
console.log('편집 취소:', err.message);
});
}
// 그리기 관련 코드
// 붙여넣기 이벤트 리스너 추가
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('이미지 편집기 초기화 완료');
};
// 수정 버튼 클릭 이벤트
$(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="guiderailSearchItem"]:checked').val() || '';
data.model_UA = $('input[name="guiderailSearchUA"]: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.rail_length = $('#rail_length').val() || '120';
data.rail_width = $('#rail_width').val() || '70';
data.check_type = $('#check_type').val() || '벽면형';
return data;
}
</script>
<script>
// 전역 변수 선언 (drawing tool 관련 변수들은 모두 제거됨)
$(document).ready(function(){
var loader = document.getElementById('loadingOverlay');
if(loader)
loader.style.display = 'none';
// ImageHandler 초기화
ImageHandler.init(guiderailImages);
// DataTables 초기 설정
dataTable = $('#myTable').DataTable({
"paging": true,
"ordering": true,
"searching": false,
"pageLength": 50, // 한 페이지에 표시할 항목 수
"lengthMenu": [ 50, 100, 200, 500, 1000], // 페이지당 표시 항목 선택 메뉴
"language": {
"lengthMenu": "Show _MENU_ entries"
},
"order": [[1, 'desc']] // 두번재 등록일 기준
});
// 페이지 번호 복원 (초기 로드 시)
var savedPageNumber = getCookie('guiderailpageNumber');
if (savedPageNumber) {
dataTable.page(parseInt(savedPageNumber) - 1).draw(false); // 쿠키에 저장된 페이지 번호로 이동
}
// 페이지 변경 이벤트 리스너
dataTable.on('page.dt', function() {
var guiderailpageNumber = dataTable.page.info().page + 1;
setCookie('guiderailpageNumber', guiderailpageNumber, 10); // 쿠키에 현재 페이지 번호 저장
});
// 페이지 길이 셀렉트 박스 변경 이벤트 처리
$('#myTable_length select').on('change', function() {
var selectedValue = $(this).val();
dataTable.page.len(selectedValue).draw(); // 페이지 길이 변경
// 변경 후 현재 페이지 번호 복원
savedPageNumber = getCookie('guiderailpageNumber');
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();
});
});
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('<?=$title_message?>');
});
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();
}
</script>
<script>
// alertToast 함수 정의
function alertToast(message) {
// 기본 배경 색상 (초록)
let backgroundColor = "linear-gradient(to right, #00b09b, #96c93d)";
// 조건에 따라 색상 변경
if (message.includes("추가")) {
backgroundColor = "linear-gradient(to right, #2196F3, #21CBF3)"; // 파란 계열
} else if (message.includes("삭제")) {
backgroundColor = "linear-gradient(to right, #f44336, #e57373)"; // 빨간 계열
} else if (message.includes("복사")) {
backgroundColor = "linear-gradient(to right, #4CAF50, #81C784)"; // 녹색 계열
}
Toastify({
text: message,
duration: 2000,
close: true,
gravity: "top",
position: "center",
style: {
background: backgroundColor
},
}).showToast();
}
// guiderail.json 파일의 내용을 JavaScript 배열로 로드
var guiderailImages = <?php
$jsonFile = $_SERVER['DOCUMENT_ROOT'].'/guiderail/guiderail.json';
echo file_exists($jsonFile) ? file_get_contents($jsonFile) : '[]';
?>;
// bending 테이블에서 부품 이미지 데이터를 가져와서 JavaScript 배열로 로드
var bendingImages = <?php
// bending 테이블에서 이미지가 있는 부품 데이터를 가져옴
$bendingSql = "SELECT item_bending, model_UA, search_keyword, itemName, material, item_sep, imgdata FROM {$DB}.bending WHERE imgdata IS NOT NULL AND imgdata != '' AND (is_deleted IS NULL OR is_deleted = '0')";
$bendingStmt = $pdo->prepare($bendingSql);
$bendingStmt->execute();
$bendingData = $bendingStmt->fetchAll(PDO::FETCH_ASSOC);
// 이미지 경로를 포함하여 배열 생성
$bendingImages = [];
foreach ($bendingData as $item) {
if (!empty($item['imgdata'])) {
$bendingImages[] = [
'item_bending' => $item['item_bending'],
'model_UA' => $item['model_UA'],
'search_keyword' => $item['search_keyword'],
'itemName' => $item['itemName'],
'material' => $item['material'],
'item_sep' => $item['item_sep'],
'image' => '/bending/img/' . $item['imgdata']
];
}
}
echo json_encode($bendingImages);
?>;
// 전역 변수로 설정하여 renderAssemblyTable에서 사용할 수 있도록 함
window.bendingImages = bendingImages;
function loadForm(mode, num = null) {
if (num == null) {
$("#mode").val('insert');
} else {
$("#mode").val(mode);
$("#num").val(num);
}
var tablename = 'guiderail';
$.ajax({
type: "POST",
url: "/guiderail/fetch_modal.php",
data: { mode: mode, num: num , tablename : tablename},
dataType: "html",
success: function(response) {
$(".modal_detail").html(response);
// 안전한 모달 표시 방법
try {
// 직접 DOM 조작으로 모달 표시 (Bootstrap 의존성 제거)
const modalElement = document.getElementById('myModal');
if (modalElement) {
// 모달 표시
modalElement.style.display = 'block';
modalElement.classList.add('show');
// body에 modal-open 클래스 추가
document.body.classList.add('modal-open');
// backdrop 추가
const backdrop = document.createElement('div');
backdrop.className = 'modal-backdrop fade show';
backdrop.style.zIndex = '1050';
document.body.appendChild(backdrop);
// 모달이 화면 중앙에 오도록 설정
modalElement.style.zIndex = '1055';
console.log('모달이 직접 DOM 조작으로 표시되었습니다.');
} else {
console.error('myModal 요소를 찾을 수 없습니다.');
}
} catch (error) {
console.error('모달 표시 오류:', error);
// 최후 수단: jQuery 사용
try {
$('#myModal').show().addClass('show');
$('body').addClass('modal-open');
$('body').append('<div class="modal-backdrop fade show"></div>');
} catch (jqueryError) {
console.error('jQuery 모달 표시 오류:', jqueryError);
}
}
// 그리기 기능 초기화 (DrawingTool 모듈 사용)
if (mode === 'view') {
console.log('view 모드에서는 그리기 기능 비활성화');
} else {
// DrawingTool 모듈 초기화
setTimeout(() => {
if (window.DrawingTool) {
window.DrawingTool.init();
console.log('DrawingTool 모듈 초기화 완료');
} else {
console.error('DrawingTool 모듈을 찾을 수 없습니다');
}
}, 200);
// 이미지 편집기 초기화 (모달이 로드된 후)
setTimeout(() => {
if (window.initializeImageEditor) {
window.initializeImageEditor();
} else {
console.warn('initializeImageEditor 함수를 찾을 수 없습니다');
}
}, 300);
}
// 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);
}
}
});
// 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(); // 초기 데이터로 테이블 렌더링 (재질별 폭합 테이블도 함께 업데이트)
$('.viewmode').prop('disabled', true); // 버튼 비활성화 false는 활성화
}
// 기존 재질별 폭합 데이터 복원
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 rail_length = $("input[name='rail_length']").val() || '';
const rail_width = $("input[name='rail_width']").val() || '';
values.item_spec = rail_length && rail_width ? `${rail_length}*${rail_width}` : '';
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 rail_length = $("input[name='rail_length']").val() || '';
const rail_width = $("input[name='rail_width']").val() || '';
const item_spec = rail_length && rail_width ? `${rail_length}*${rail_width}` : '';
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(); // 재질별 폭합 테이블도 함께 표시
// $("#assemblyModelArea").hide();
// $("#materialSummaryArea").hide(); // 재질별 폭합 테이블도 함께 숨김
}
});
// 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', '#searchGuiderailBtn', function(e) {
e.preventDefault();
// 안전한 중첩 모달 표시 방법
try {
const modalElement = document.getElementById('guiderailSearchModal');
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() {
// 제품모델 목록 로드
loadGuiderailModels();
// searchGuiderailItems 함수가 존재하는지 확인 후 호출
if (typeof searchGuiderailItems === 'function') {
searchGuiderailItems();
} else {
console.error('searchGuiderailItems 함수를 찾을 수 없습니다.');
}
}, 100);
} else {
console.error('guiderailSearchModal 요소를 찾을 수 없습니다.');
}
} catch (error) {
console.error('가이드레일 중첩 모달 표시 오류:', error);
// 최후 수단: jQuery 사용
try {
$('#guiderailSearchModal').show().addClass('show');
$('#guiderailSearchModal').css('z-index', '1065');
$('body').append('<div class="modal-backdrop fade show" style="z-index: 1060;"></div>');
setTimeout(function() {
loadGuiderailModels();
if (typeof searchGuiderailItems === 'function') {
searchGuiderailItems();
}
}, 100);
} catch (jqueryError) {
console.error('jQuery 가이드레일 중첩 모달 표시 오류:', jqueryError);
}
}
});
// 제품모델 목록을 로드하는 함수
function loadGuiderailModels() {
$.ajax({
url: '/guiderail/get_guiderail_models.php',
type: 'GET',
success: function(data) {
const modelSelect = $('#searchGuiderailModel');
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: "/guiderail/search_guiderail.php", // searchbending.php -> search_guiderail.php로 수정
type: 'GET',
data: searchData,
success: function(htmlResponse) {
console.log('=== 검색 성공 ===');
console.log('검색 결과 HTML 길이:', htmlResponse.length);
console.log('검색 결과 HTML:', htmlResponse.substring(0, 500) + '...');
const tableBody = $("#bendingSearchResults");
tableBody.empty();
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('=== 검색 완료 ===');
},
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 updateOrderNumbers() {
const $rows = $('#bendingSearchResults tr');
// 모든 순서 번호 초기화
$rows.each(function() {
const $orderCell = $(this).find('.order-number');
if ($orderCell.length > 0) {
$orderCell.text('-');
}
});
// 클릭 순서에 따라 순서 번호 부여
checkboxClickOrder.forEach(function(rowIndex, orderIndex) {
const $row = $rows.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);
}
}
});
// 디버깅용 로그
console.log('checkboxClickOrder:', checkboxClickOrder);
console.log('체크된 행 수:', $rows.find('.bending-item-checkbox:checked').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');
}
// 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();
});
}
// 조립 부품 테이블에서 체크박스 기반 순서 변경 기능
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('부품 정보 업데이트에 실패했습니다.');
}
};
// 저장 버튼 이벤트
$("#saveBtn").off("click").on("click", function() {
console.log('saveBtn 클릭');
// 이미지 데이터 가져오기
const targetImg = document.getElementById('targetImage');
if (targetImg && targetImg.src) {
const imageDataUrl = targetImg.src;
console.log('imageDataUrl : ' , imageDataUrl);
// 1) 업로드용 input 요소 생성/가져오기
let fileInput = document.getElementById('upfile');
if (!fileInput) {
fileInput = document.createElement('input');
fileInput.type = 'file';
fileInput.id = 'upfile';
fileInput.name = 'upfile';
fileInput.style.display = 'none';
document.querySelector('#myModal .modal-body').appendChild(fileInput);
}
// 2) 이미지 데이터를 File 객체로 변환
fetch(imageDataUrl)
.then(res => res.blob())
.then(blob => {
const file = new File([blob], 'drawing.png', { type: 'image/png' });
const dt = new DataTransfer();
dt.items.add(file);
fileInput.files = dt.files;
// 3) 검색 조건 데이터 구성
const payload = {
firstitem: $('input[name="firstitem"]:checked').val(),
UA: $('input[name="model_UA"]:checked').val(),
model_name: $('#model_name').val(),
check_type: $('input[name="check_type"]:checked').val(),
finishing_type: $('#finishing_type').val(),
search_keyword: $('#search_keyword').val(),
};
// 4) 동일 조건의 기존 이미지 검색
const existingIndex = guiderailImages.findIndex(item => {
const matchBase =
item.firstitem === payload.firstitem &&
item.UA === payload.UA &&
item.model_name === payload.model_name &&
item.check_type === payload.check_type &&
item.finishing_type === payload.finishing_type;
if (!matchBase) return false;
if (payload.search_keyword) {
return item.search_keyword === payload.search_keyword;
}
return true;
});
// 5) 이미지와 폼 데이터 저장 함수
function saveAll(overwrite = false) {
// 이미지 저장 FormData
const imageData = new FormData();
Object.entries(payload).forEach(([k,v]) => imageData.append(k, v));
imageData.append('upfile', file);
if (overwrite && existingIndex > -1) {
imageData.append('index', existingIndex);
}
// 폼 데이터 준비
$("#bending_components_data").val(JSON.stringify(assemblyParts));
if (material_summary && Object.keys(material_summary).length > 0) {
$("#material_summary").val(JSON.stringify(material_summary));
} else {
$("#material_summary").val('');
}
// 이미지 저장 후 폼 데이터 저장
$.ajax({
url: '/guiderail/put_guiderail_image.php',
type: 'POST',
data: imageData,
processData: false,
contentType: false,
dataType: 'json',
success(res) {
if (res.success) {
// 이미지 저장 성공 시 폼 데이터 저장
$.ajax({
url: "/guiderail/process.php",
type: "post",
data: $("#board_form").serialize(),
dataType: 'json',
success: function(response) {
Toastify({
text: "저장완료",
duration: 1500,
close: true,
gravity: "top",
position: "center",
backgroundColor: "#4fbe87",
}).showToast();
setTimeout(function() {
window.close();
location.reload();
}, 1000);
}
});
} else {
alert('이미지 저장 중 오류: ' + res.error);
}
}
});
}
// 6) 기존 이미지 존재 시 덮어쓰기 확인
if (existingIndex > -1) {
Swal.fire({
title: '이미지 덮어쓰기',
text: '동일한 조건의 이미지가 이미 존재합니다. 덮어쓰시겠습니까?',
icon: 'warning',
showCancelButton: true,
confirmButtonColor: '#3085d6',
cancelButtonColor: '#6c757d',
confirmButtonText: '덮어쓰기',
cancelButtonText: '취소'
}).then((result) => {
if (result.isConfirmed) {
saveAll(true);
}
});
} else {
saveAll(false);
}
});
} else {
// 이미지가 없는 경우 폼 데이터만 저장
$("#bending_components_data").val(JSON.stringify(assemblyParts));
if (material_summary && Object.keys(material_summary).length > 0) {
$("#material_summary").val(JSON.stringify(material_summary));
} else {
$("#material_summary").val('');
}
$.ajax({
url: "/guiderail/process.php",
type: "post",
data: $("#board_form").serialize(),
dataType: 'json',
success: function(response) {
Toastify({
text: "저장완료",
duration: 1500,
close: true,
gravity: "top",
position: "center",
backgroundColor: "#4fbe87",
}).showToast();
setTimeout(function() {
window.close();
location.reload();
}, 1000);
}
});
}
});
// 14. 삭제 버튼 이벤트
$("#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: "/guiderail/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').modal('hide');
location.reload();
},
error: function(jqxhr, status, error) {
console.log(jqxhr, status, error);
}
});
}
});
});
// 가이드레일 선택에 따른 동작 구현
const checkImage = $('#checkImage');
const prodModelSelect = $('select[name="model_name"]');
const finishing_typeSelect = $('select[name="finishing_type"]');
// 선택된 값에 따라 이미지 설정
const initialCheckType = $("#model_name").val();
// console.log('선택모델 : ' , $("#model_name").val());
// console.log('선택마감 : ' , $("#finishing_type").val());
ImageHandler.updateImage( $("#model_name").val(), $('input[name="check_type"]:checked').val() , $("#finishing_type").val());
// 라디오 버튼에 변화가 생길 때 이미지 업데이트
$('input[name="check_type"]').on('change', function() {
const selectedCheckType = $(this).val();
const selectedfinishing_type = $("#finishing_type").val();
ImageHandler.updateImage(prodModelSelect.val(), selectedCheckType, selectedfinishing_type); // check_type과 model_name 둘 다 전달
});
// select 요소가 변경될 때 이미지 업데이트
prodModelSelect.on('change', function() {
const selectedCheckType = $('input[name="check_type"]:checked').val();
const selectedfinishing_type = $("#finishing_type").val();
ImageHandler.updateImage($(this).val(), selectedCheckType, selectedfinishing_type); // check_type과 model_name 둘 다 전달
});
// select 요소가 변경될 때 이미지 업데이트
finishing_typeSelect.on('change', function() {
const initialModel = $("#model_name").val();
const selectedCheckType = $('input[name="check_type"]:checked').val();
const selectedfinishing_type = $("#finishing_type").val();
ImageHandler.updateImage(initialModel, selectedCheckType, selectedfinishing_type); // check_type과 model_name 둘 다 전달
});
$('#display_rail_length').val($('#rail_length').val());
$('#display_rail_width').val($('#rail_width').val());
// rail_length 입력이 변경될 때 아래 요소에 반영
$('#rail_length').on('input', function() {
$('#display_rail_length').val($(this).val());
});
// rail_width 입력이 변경될 때 아래 요소에 반영
$('#rail_width').on('input', function() {
$('#display_rail_width').val($(this).val());
});
$("#copyBtn").on("click", function() { // 복사버튼
var num = $(this).data("num"); // 'data-num' 속성 값 가져오기
// 안전한 모달 닫기 방법
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 조작으로 닫혔습니다.');
} 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);
}
}
$(".modal_detail").html(''); // 모달 내용을 비움
loadForm('copy', num);
});
$("#modifyBtn").on("click", function() { // 수정버튼
var num = $(this).data("num"); // 'data-num' 속성 값 가져오기
// 안전한 모달 닫기 방법
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 조작으로 닫혔습니다.');
} 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);
}
}
$(".modal_detail").html(''); // 모달 내용을 비움
loadForm('modify', num);
});
// 조회인 경우 사용금지 처리
if (mode === 'view') {
$('.viewmode').prop('readonly', true);
// data-readonly 속성이 없는 viewmode 요소만 disabled 처리
// checkbox와 radio는 클릭 불가능하게 하고 시각적 강조
$('input[type="checkbox"], input[type="radio"]').each(function() {
$(this).addClass('readonly-checkbox readonly-radio');
});
} else {
$('.viewmode').prop('disabled', false).css({
'pointer-events': 'auto',
'opacity': '1'
});
}
let isSaving = false;
},
error: function(jqxhr, status, error) {
console.log("AJAX Error: ", status, error);
}
});
}
function viewWork(num) {
// 이벤트의 기본 동작을 방지 (모달창 닫힘 방지)
event.preventDefault();
var tablename = 'guiderail'
var url = "view.php?mode=view&num=" + num + "&tablename=" + tablename;
customPopup(url, '절곡 작업지시서', 1200, 850);
}
// 테이블을 다시 그린 직후, 또는 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 searchGuiderailItems() {
console.log('=== 가이드레일 검색 함수 시작 ===');
const searchText = $('#guiderailSearchInput').val();
// 라디오 버튼 값 읽기 (대분류, 인정/비인정)
const selectedItem = $('input[name="guiderailSearchItem"]:checked').val(); // 대분류(스크린/철재)
const selectedUA = $('input[name="guiderailSearchUA"]:checked').val(); // 인정/비인정
const selectedModel = $('#searchGuiderailModel').val(); // 제품모델
const selectedType = $('#searchGuiderailType').val(); // 형태
const selectedFinishing = $('#searchGuiderailFinishing').val(); // 마감
// 디버깅: 검색 조건 로그 출력
console.log('가이드레일 검색 조건:', {
searchText,
selectedItem,
selectedUA,
selectedModel,
selectedType,
selectedFinishing
});
const searchData = {
searchItem: selectedItem,
searchUA: selectedUA,
searchGuiderailModel: selectedModel,
searchGuiderailType: selectedType,
searchGuiderailFinishing: 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:', "/guiderail/search_guiderail_json.php");
$.ajax({
url: "/guiderail/search_guiderail_json.php",
type: 'GET',
data: searchData,
success: function(htmlResponse) {
console.log('=== 가이드레일 검색 성공 ===');
console.log('검색 결과 HTML 길이:', htmlResponse.length);
const tableBody = $("#guiderailSearchResults");
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;
}
// 선택된 행 스타일 변경
$('#guiderailSearchResults 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);
$("#guiderailSearchResults").html('<tr><td colspan="9" class="text-center text-danger">검색 중 오류가 발생했습니다.</td></tr>');
}
});
}
// 가이드레일 검색 모달 이벤트 핸들러들
$(document).ready(function() {
// 가이드레일 검색 조건 변경 이벤트
$(document).on('click change', '#guiderailSearchBtn, input[name="searchItem"], input[name="searchUA"], #searchGuiderailModel, #searchGuiderailType, #searchGuiderailFinishing', function(e) {
e.preventDefault();
console.log('가이드레일 검색 조건 변경됨:', $(this).attr('id'), $(this).val());
searchGuiderailItems();
});
// 가이드레일 검색어 엔터키 이벤트
$(document).on('keypress', '#guiderailSearchInput', function(e) {
if (e.which === 13) {
e.preventDefault();
console.log('가이드레일 검색어 엔터키 입력됨');
searchGuiderailItems();
}
});
// 가이드레일 검색 조건 초기화 버튼
$(document).on('click', '#resetGuiderailSearchBtn', function(e) {
e.preventDefault();
// 라디오 버튼 초기화
$('#guiderailSearchModal input[name="searchItem"]').prop('checked', false);
$('#guiderailSearchModal input[name="searchUA"]').prop('checked', false);
$('#searchItem_all').prop('checked', true);
$('#searchUA_all').prop('checked', true);
// select 박스 초기화
$('#guiderailSearchModal .form-select, #guiderailSearchInput').val('');
console.log('가이드레일 검색 조건 초기화됨');
searchGuiderailItems();
});
// 가이드레일 행 클릭 시 바로 적용
$(document).on('click', '#guiderailSearchResults tr', function(e) {
// 이미지 클릭은 제외
if ($(e.target).is('img')) {
return;
}
// 선택된 행 스타일 변경
$('#guiderailSearchResults tr').removeClass('selected table-active');
$(this).addClass('selected table-active');
// 선택된 행의 인덱스 가져오기
const index = $(this).data('index');
console.log('선택된 인덱스:', index);
console.log('guiderailImages 배열:', guiderailImages);
if (index !== undefined && index !== null) {
// 전역 guiderailImages 변수 사용 (이미 정의됨)
if (guiderailImages && Array.isArray(guiderailImages)) {
// data-index는 1부터 시작하므로 0부터 시작하는 배열 인덱스로 변환
const arrayIndex = index - 1;
console.log('배열 인덱스:', arrayIndex);
console.log('guiderailImages 길이:', guiderailImages.length);
if (arrayIndex >= 0 && arrayIndex < guiderailImages.length) {
const guiderailData = guiderailImages[arrayIndex];
console.log('적용할 가이드레일 데이터:', guiderailData);
ImageHandler.applyGuiderailData(guiderailData);
// 안전한 중첩 모달 닫기 방법
try {
const modalElement = document.getElementById('guiderailSearchModal');
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('guiderailSearchModal 요소를 찾을 수 없습니다.');
}
} catch (error) {
console.error('가이드레일 중첩 모달 닫기 오류:', error);
// 최후 수단: jQuery 사용
try {
$('#guiderailSearchModal').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', '#guiderailSearchModal', function() {
console.log('가이드레일 검색 모달이 닫혔습니다. 초기화를 실행합니다.');
// 검색 결과 초기화
$('#guiderailSearchResults').empty();
// 검색 조건 초기화
$('#guiderailSearchModal .form-select, #guiderailSearchInput').val('');
// 라디오 버튼 초기화
$('#guiderailSearchModal input[name="searchItem"]').prop('checked', false);
$('#guiderailSearchModal input[name="searchUA"]').prop('checked', false);
$('#searchItem_all').prop('checked', true);
$('#searchUA_all').prop('checked', true);
$('#guiderailSearchModal input[type="checkbox"]').prop('checked', false);
// 중첩 모달 처리를 위한 배경 초기화
$('.modal-backdrop').each(function() {
if ($(this).css('z-index') > 1050) {
$(this).remove();
}
});
// 모달 스타일 초기화
$('#guiderailSearchModal').removeClass('show').css({
'display': 'none',
'background-color': 'transparent',
'z-index': 'auto'
});
// 부모 모달이 있다면 부모 모달 복원
const parentModal = $('.modal.show').not('#guiderailSearchModal');
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', '#closeGuiderailSearchModal, #cancelGuiderailSearchModal', function() {
// 안전한 중첩 모달 닫기 방법
try {
const modalElement = document.getElementById('guiderailSearchModal');
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('guiderailSearchModal 요소를 찾을 수 없습니다.');
}
} catch (error) {
console.error('가이드레일 중첩 모달 닫기 오류:', error);
// 최후 수단: jQuery 사용
try {
$('#guiderailSearchModal').hide().removeClass('show');
$('.modal-backdrop[style*="z-index: 1060"]').remove();
} catch (jqueryError) {
console.error('jQuery 가이드레일 중첩 모달 닫기 오류:', jqueryError);
}
}
});
// 가이드레일 검색 모달용 CSS 스타일 추가
$('<style>')
.prop('type', 'text/css')
.html(`
#guiderailSearchResults tr {
transition: background-color 0.2s ease;
}
#guiderailSearchResults tr:hover {
background-color: #f8f9fa !important;
}
#guiderailSearchResults tr.selected {
background-color: #007bff !important;
color: white !important;
}
#guiderailSearchResults tr.selected:hover {
background-color: #0056b3 !important;
}
`)
.appendTo('head');
// 가이드레일 검색 모달 HTML 추가
$(document).ready(function() {
$('body').append(`
<div id="guiderailSearchModal" 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="closeGuiderailSearchModal" 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="guiderailSearchItem" id="guiderailSearchItem_all" value="" checked>
<label class="btn btn-outline-secondary btn-sm" for="guiderailSearchItem_all">전체</label>
<input type="radio" class="btn-check" name="guiderailSearchItem" id="guiderailSearchItem_스크린" value="스크린">
<label class="btn btn-outline-primary btn-sm" for="guiderailSearchItem_스크린">스크린</label>
<input type="radio" class="btn-check" name="guiderailSearchItem" id="guiderailSearchItem_철재" value="철재">
<label class="btn btn-outline-primary btn-sm" for="guiderailSearchItem_철재">철재</label>
</div>
<!-- 인정/비인정 라디오 버튼 -->
<div class="btn-group" role="group" aria-label="인정/비인정 선택" style="font-size: 0.9rem;">
<input type="radio" class="btn-check" name="guiderailSearchUA" id="guiderailSearchUA_all" value="" checked>
<label class="btn btn-outline-secondary btn-sm" for="guiderailSearchUA_all">전체</label>
<input type="radio" class="btn-check" name="guiderailSearchUA" id="guiderailSearchUA_인정" value="인정">
<label class="btn btn-outline-success btn-sm" for="guiderailSearchUA_인정">인정</label>
<input type="radio" class="btn-check" name="guiderailSearchUA" id="guiderailSearchUA_비인정" value="비인정">
<label class="btn btn-outline-success btn-sm" for="guiderailSearchUA_비인정">비인정</label>
</div>
<!-- 제품모델 -->
<select id="searchGuiderailModel" name="searchGuiderailModel" class="form-select" style="width: auto; min-width: 120px; font-size: 0.9rem;">
<option value="">(제품모델)</option>
</select>
<!-- 형태 -->
<select id="searchGuiderailType" name="searchGuiderailType" class="form-select" style="width: auto; min-width: 120px; font-size: 0.9rem;">
<option value="">(형태)</option>
<option value="벽면형">벽면형</option>
<option value="측면형">측면형</option>
</select>
<!-- 마감 -->
<select id="searchGuiderailFinishing" name="searchGuiderailFinishing" 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="guiderailSearchInput" placeholder="품목검색어 입력" style="font-size: 0.9rem; height:35px!important;">
<button class="btn btn-primary" type="button" id="guiderailSearchBtn" style="font-size: 0.9rem;"><i class="bi bi-search"></i> 검색</button>
</div>
<!-- 검색 조건 초기화 버튼 -->
<button class="btn btn-outline-secondary" type="button" id="resetGuiderailSearchBtn" 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>
</tr>
</thead>
<tbody id="guiderailSearchResults">
<!-- 검색 결과가 여기에 표시됩니다 -->
</tbody>
</table>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" id="cancelGuiderailSearchModal">취소</button>
</div>
</div>
</div>
</div>
`);
});
$(document).on("click", ".registImageBtn", function() {
popupCenter('guideraillist.php' ,'' , 1200, 900);
});
// 그리기 기능 관련 이벤트 핸들러들 (shutterbox 스타일)
// 모달이 열린 후 그리기 기능 초기화
$(document).on('shown.bs.modal', '#myModal', function() {
// mode가 view면 그리기 기능 초기화하지 않음
if ($('#mode').val() === 'view') {
console.log('view 모드에서는 그리기 기능 비활성화');
return;
}
// 그리기 기능 초기화
setTimeout(() => {
if (typeof initializeDrawingFeatures === 'function') {
initializeDrawingFeatures();
}
}, 200);
});
// 미니 합계표 팝업 기능은 ImageHandler 모듈에서 처리됨
// 라디오 버튼 변경 시 검색
$(document).on('change', 'input[name="guiderailSearchItem"]', function() {
searchGuiderailItems();
});
$(document).on('change', 'input[name="guiderailSearchUA"]', function() {
searchGuiderailItems();
});
$(document).on("click", "#modelFlatBtn", function() {
window.open('/guiderail/model_flat.php', 'modelFlat', 'width=1200,height=900,scrollbars=yes');
});
// 2. 모달이 완전히 닫힌 후 실행될 모든 정리 작업을 여기에 모읍니다.
$(document).on('hidden.bs.modal', '#myModal', function() {
// 비활성화된 요소들 다시 활성화
$('.viewmode').prop('disabled', false);
// 모달 내용 비우기
$(".modal_detail").html('');
});
// 테이블 정렬 기능
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();
// 이전 정렬 헤더에서 active 클래스 제거
$('.sortable-header').removeClass('active sort-asc sort-desc').addClass('sort-default');
// 현재 헤더에 active 클래스 추가
$(this).removeClass('sort-default').addClass('active');
// 정렬 방향 결정
if (currentSort.column === sortType && currentSort.direction === 'asc') {
currentSort.direction = 'desc';
$(this).addClass('sort-desc');
} else {
currentSort.direction = 'asc';
$(this).addClass('sort-asc');
}
currentSort.column = sortType;
// 행 정렬
$rows.sort(function(a, b) {
let aValue, bValue;
switch(sortType) {
case 'date':
// 등록일 정렬 (첫 번째 td의 텍스트)
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 'material':
// 재질 정렬
aValue = $(a).find('td:eq(7)').text().trim();
bValue = $(b).find('td:eq(7)').text().trim();
break;
case 'width':
// 폭합 정렬 (숫자로 변환)
aValue = parseFloat($(a).find('td:eq(9)').text().trim()) || 0;
bValue = parseFloat($(b).find('td:eq(9)').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 $table = $('.sortable-header').closest('table');
const $rows = $table.find('tbody tr');
// 모든 순서 번호 초기화
$rows.each(function() {
const $orderCell = $(this).find('.order-number');
if ($orderCell.length > 0) {
$orderCell.text('-');
}
});
// 클릭 순서에 따라 순서 번호 부여
checkboxClickOrder.forEach(function(rowIndex, orderIndex) {
const $row = $rows.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>