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