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

1598 lines
72 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

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

<?php
require_once($_SERVER['DOCUMENT_ROOT'] . "/session.php");
if (!isset($_SESSION["level"]) || $_SESSION["level"] > 5) {
sleep(1);
header("Location:" . $WebSite . "login/login_form.php");
exit;
}
include $_SERVER['DOCUMENT_ROOT'] . '/load_header.php';
// 첫 화면 표시 문구
$title_message = '절곡바라시 기초자료';
?>
<title> <?=$title_message?> </title>
<link rel="stylesheet" href="css/style.css?v=<?=time()?>">
</head>
<body>
<?php
$option = isset($_REQUEST['option']) ? $_REQUEST['option'] : '';
$search = isset($_REQUEST['search']) ? $_REQUEST['search'] : '';
$mode = isset($_REQUEST['mode']) ? $_REQUEST['mode'] : '';
$header = isset($_REQUEST['header']) ? $_REQUEST['header'] : '';
$partIndex = isset($_REQUEST['partIndex']) ? $_REQUEST['partIndex'] : '';
$tablename = 'bending';
require_once($_SERVER['DOCUMENT_ROOT'] . "/lib/mydb.php");
$pdo = db_connect();
$num = isset($_REQUEST["num"]) ? $_REQUEST["num"] : 0;
// 조회일 경우
if ($num > 0 && $mode == 'view' ) {
try {
$sql = "SELECT * FROM " . $DB . "." . $tablename . " WHERE num=?";
$stmh = $pdo->prepare($sql);
$stmh->bindValue(1, $num, PDO::PARAM_STR);
$stmh->execute();
$row = $stmh->fetch(PDO::FETCH_ASSOC);
include '_row.php';
} catch (PDOException $Exception) {
print "오류: " . $Exception->getMessage();
}
$mode = 'view';
$title_message = '절곡바라시 기초자료 조회';
}
else if ($num > 0 && $mode == 'modify' ) {
try {
$sql = "SELECT * FROM " . $DB . "." . $tablename . " WHERE num=?";
$stmh = $pdo->prepare($sql);
$stmh->bindValue(1, $num, PDO::PARAM_STR);
$stmh->execute();
$row = $stmh->fetch(PDO::FETCH_ASSOC);
include '_row.php';
} catch (PDOException $Exception) {
print "오류: " . $Exception->getMessage();
}
$mode = 'modify';
$title_message = '절곡바라시 기초자료 수정';
}
else if ($num > 0 && $mode == 'copy' ) {
try {
$sql = "SELECT * FROM " . $DB . "." . $tablename . " WHERE num=?";
$stmh = $pdo->prepare($sql);
$stmh->bindValue(1, $num, PDO::PARAM_STR);
$stmh->execute();
$row = $stmh->fetch(PDO::FETCH_ASSOC);
include '_row.php';
} catch (PDOException $Exception) {
print "오류: " . $Exception->getMessage();
}
$title_message = '(데이터복사) 절곡바라시 기초자료 등록';
}
else if ($mode == 'write' ) { // 전달받은 새로 작성하는 경우
include '_request.php';
$imgdata = '';
// URL 파라미터에서 배열 데이터 받기
$inputList = isset($_REQUEST['inputList']) ? json_decode($_REQUEST['inputList'], true) : [];
$bendingrateList = isset($_REQUEST['bendingrateList']) ? json_decode($_REQUEST['bendingrateList'], true) : [];
$sumList = isset($_REQUEST['sumList']) ? json_decode($_REQUEST['sumList'], true) : [];
$colorList = isset($_REQUEST['colorList']) ? json_decode($_REQUEST['colorList'], true) : [];
$AList = isset($_REQUEST['AList']) ? json_decode($_REQUEST['AList'], true) : [];
// 이미지는 부모창의 번호를 기준으로 가져온다.
if(intval($num) > 0) {
$sql = "SELECT * FROM " . $DB . "." . $tablename . " WHERE num=?";
$stmh = $pdo->prepare($sql);
$stmh->bindValue(1, $num, PDO::PARAM_STR);
$stmh->execute();
$row = $stmh->fetch(PDO::FETCH_ASSOC);
$imgdata = $row['imgdata'];
}
// 부모창에서 전달받은 partIndex (부품 인덱스)
$partIndex = isset($_REQUEST['partIndex']) ? $_REQUEST['partIndex'] : '';
$author = isset($user_name) ? $user_name : '';
$registration_date = date('Y-m-d'); // 현재일자 기록
$title_message = '(데이터 다른이름으로 저장) 절곡바라시 기초자료 등록';
} else {
include '_request.php';
$mode = 'insert';
$memo = '';
$imgdata = '';
$itemName = '';
$inputList = [];
$bendingrateList = [];
$sumList = [];
$colorList = [];
$AList = [];
$author = $user_name ?? '';
$registration_date = date('Y-m-d'); // 현재일자 기록
// mode값에 get 이면 각 부품에서 이 코드를 호출하는 것이다.
if($mode == 'get') {
// 삽입으로 설정함.
$mode = 'insert';
$item_sep = isset($_REQUEST['item_sep']) ? $_REQUEST['item_sep'] : '';
$model_UA = isset($_REQUEST['model_UA']) ? $_REQUEST['model_UA'] : '';
$item_bending = isset($_REQUEST['item_bending']) ? $_REQUEST['item_bending'] : '';
$itemName = isset($_REQUEST['itemName']) ? $_REQUEST['itemName'] : '';
$item_spec = isset($_REQUEST['item_spec']) ? $_REQUEST['item_spec'] : '';
$imgdata = isset($_REQUEST['imgdata']) ? $_REQUEST['imgdata'] : '';
$exit_direction = isset($_REQUEST['exit_direction']) ? $_REQUEST['exit_direction'] : '';
$front_bottom_width = isset($_REQUEST['front_bottom_width']) ? $_REQUEST['front_bottom_width'] : '';
$rail_width = isset($_REQUEST['rail_width']) ? $_REQUEST['rail_width'] : '';
$box_width = isset($_REQUEST['box_width']) ? $_REQUEST['box_width'] : '';
$box_height = isset($_REQUEST['box_height']) ? $_REQUEST['box_height'] : '';
}
}
if(empty($author)) {
$author = $user_name ?? '';
}
?>
<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" value=<?=$num?>>
<input type="hidden" id="tablename" name="tablename" value=<?=$tablename?>>
<input type="hidden" id="update_log" name="update_log" value=<?=$update_log?>>
<input type="hidden" id="header" name="header" value="<?=$header?>">
<input type="hidden" id="imgdata" name="imgdata" value="<?=$imgdata?>">
<div class="container-fluid">
<div class="card">
<div class="card-header text-center">
<div class="d-flex p-1 mb-1 justify-content-center align-items-center ">
<h4><?=$title_message?></h4> &nbsp; &nbsp; &nbsp; &nbsp;
<?php if($mode =='view') { ?>
<button type="button" class="btn btn-dark btn-sm me-1" onclick="location.href='write_form.php?mode=modify&num=<?=$num?>&tablename=<?=$tablename?>';"> <i class="bi bi-pencil-square"></i> 수정 </button>
<button id="copyBtn" class="btn btn-primary btn-sm me-1" type="button"><i class="bi bi-copy"></i> 복사</button>
<button id="deleteBtn" class="btn btn-danger btn-sm me-1" type="button"><i class="bi bi-trash2"></i> 삭제</button>
<?php } ?>
<?php if($mode!=='view') { ?>
<button id="saveBtn" class="btn btn-dark btn-sm me-1 " type="button">
<? if((int)$num>0) print ' <i class="bi bi-hdd-fill"></i> 저장'; else print ' <i class="bi bi-hdd-fill"></i> 저장'; ?></button>
<?php } ?>
<button type="button" class="btn btn-outline-dark btn-sm me-2" id="showlogBtn"> H</button>
<button class="btn btn-secondary btn-sm ms-3" onclick="self.close();"> &times; 닫기 </button>
</div>
</div>
<div class="card-body">
<div class="container-fluid">
<div class="card">
<div class="card-body">
<div class="row mt-3">
<div class="col-sm-9">
<div class="card">
<div class="card-header text-center">
<table class="table table-bordered align-middle">
<tr>
<!-- 1행: 등록일, SLAT형태, 절곡품 그룹 -->
<td class="text-end align-middle fw-bold" style="width: 8%;">등록일</td>
<td style="width: 13%;">
<input type="date" id="registration_date" class="form-control noborder-input w110px" name="registration_date" value="<?= $registration_date ?>" autocomplete="off">
</td>
<td class="text-end align-middle fw-bold" style="width: 8%;">형태</td>
<td class="w250px">
<div class="d-flex justify-content-start">
<input type="radio" id="screenUse" name="item_sep" value="스크린" <?php if (empty($item_sep) || $item_sep == '스크린') echo 'checked'; ?> class="me-1">
<label for="screenUse" class="me-2">스크린</label>
<input type="radio" id="steelStrutUse" name="item_sep" value="철재" <?php if ($item_sep == '철재') echo 'checked'; ?> class="me-1">
<label for="steelStrutUse">철재</label>
</div>
</td>
<td class="text-end align-middle fw-bold" style="width: 10%;">인정/비인정</td>
<td class="w150px">
<input type="radio" id="screenUseUA" name="model_UA" value="인정" <?php if (empty($model_UA) || $model_UA == '인정') echo 'checked'; ?> class="me-1">
<label for="screenUseUA" class="me-2">인정</label>
<input type="radio" id="steelStrutUseUA" name="model_UA" value="비인정" <?php if ($model_UA == '비인정') echo 'checked'; ?> class="me-1">
<label for="steelStrutUseUA">비인정</label>
</td>
<td class="text-end align-middle fw-bold" style="width: 8%;">절곡품 그룹</td>
<td colspan="1" class="text-start">
<?php
// 예: getCategoryByName($parentName) 함수가 존재하며,
// '절곡물'이라는 2단계 카테고리의 자식(3단계) 카테고리 이름 배열을 리턴한다.
$l3_list = getCategoryByName('스크린','절곡물');
?>
<select id="item_bending" name="item_bending" class="form-select w-auto mx-1" style="font-size: 0.7rem; height: 30px;">
<option value="">(절곡물)</option>
<?php foreach($l3_list as $itemVal): ?>
<option
value="<?= htmlspecialchars($itemVal, ENT_QUOTES, 'UTF-8') ?>"
<?= ($item_bending === $itemVal) ? 'selected' : '' ?>>
<?= htmlspecialchars($itemVal, ENT_QUOTES, 'UTF-8') ?>
</option>
<?php endforeach; ?>
</select>
</td>
</tr>
<tr>
<td class="text-end align-middle fw-bold w50px">폭합</td>
<td colspan="1">
<input type="text" id="widthsum" class="form-control text-end w50px" name="widthsum" value="<?= $widthsum ?>" readonly autocomplete="off">
</td>
<td class="text-end align-middle fw-bold">품명</td>
<td>
<input type="text" id="itemName" class="form-control noborder-input text-start" name="itemName" value="<?= $itemName ?>" autocomplete="off">
</td>
<td class="text-end align-middle fw-bold">규격(가로*세로)</td>
<td>
<input type="text" id="item_spec" class="form-control noborder-input text-start " name="item_spec" value="<?= $item_spec ?>" autocomplete="off">
</td>
<td class="text-end align-middle fw-bold">재질</td>
<td>
<select id="material" class="form-select w-auto" name="material" style="font-size: 0.7rem; height: 30px;">
<?php
$options = ['(선택)', 'EGI 1.15T', 'EGI 1.55T', 'SUS 1.2T', 'SUS 1.5T'];
foreach ($options as $option) {
$selected = ($material === $option) ? 'selected' : '';
echo "<option value=\"" . htmlspecialchars($option, ENT_QUOTES, 'UTF-8') . "\" $selected>$option</option>";
}
?>
</select>
</td>
</tr>
<tr>
<td colspan="8" class="text-end ">
<div class="d-flex justify-content-start align-items-center">
(케이스 부품관련 영역) <span class="text-dark ms-5 me-2"></span>점검구 방향</span>
<select id="exit_direction" class="form-select w120px" name="exit_direction" style="font-size: 0.7rem; height: 30px;">
<option value="">(선택)</option>
<option value="양면 점검구" <?php if ($exit_direction === '양면 점검구') echo 'selected'; ?>>양면 점검구</option>
<option value="후면 점검구" <?php if ($exit_direction === '후면 점검구') echo 'selected'; ?>>후면 점검구</option>
<option value="밑면 점검구" <?php if ($exit_direction === '밑면 점검구') echo 'selected'; ?>>밑면 점검구</option>
</select>
<span class="text-dark ms-3 me-1">케이스 너비</span>
<input type="text" id="box_width" class="form-control text-end w50px" name="box_width" value="<?= $box_width ?>" autocomplete="off">
<span class="text-dark ms-3 me-1">케이스 높이</span>
<input type="text" id="box_height" class="form-control text-end w50px" name="box_height" value="<?= $box_height ?>" autocomplete="off">
<span class="text-dark ms-3 me-1">전면부 밑 치수</span>
<input type="text" id="front_bottom_width" class="form-control text-end w50px" name="front_bottom_width" value="<?= $front_bottom_width ?>" autocomplete="off">
<span class="text-dark ms-3 me-1">레일폭</span>
<input type="text" id="rail_width" class="form-control text-end w50px" name="rail_width" value="<?= $rail_width ?>" autocomplete="off">
</div>
</td>
</tr>
<tr>
<td class="text-end align-middle fw-bold">작성자</td>
<td colspan="1">
<input type="text" id="author" class="form-control noborder-input text-start" name="author" value="<?= $author ?>" autocomplete="off">
</td>
<td class="text-end align-middle fw-bold">품목 검색어</td>
<td colspan="1">
<input type="text" id="search_keyword" class="form-control noborder-input text-start" name="search_keyword" value="<?= $search_keyword ?>" autocomplete="off">
</td>
<td class="text-end align-middle fw-bold">비고</td>
<td colspan="3">
<input type="text" id="memo" class="form-control noborder-input text-start" name="memo" value="<?= $memo ?>" autocomplete="off">
</td>
</tr>
</table>
</div>
<div class="card-body">
<div class="row">
<div class="table-container">
<table class="table table-bordered" id="dynamicTable">
<tbody id="tableBody">
<!-- 기본적으로 빈 상태로 두고, JS로 데이터를 추가 -->
</tbody>
</table>
</div>
</div>
<div class="d-flex mt-2 mb-2">
<button type="button" class="btn btn-secondary btn-sm me-2" id="emptyColumnBtn">모든칸 비우기</button>
<button type="button" class="btn btn-success btn-sm me-2" id="addColumnBtn">마지막 열추가</button>
<button type="button" class="btn btn-danger btn-sm" id="removeColumnBtn">마자막 열삭제</button>
</div>
</div>
</div>
</div>
<div class="col-sm-3" style="padding : 4px;">
<!-- 그리기 컨트롤 -->
<div class="d-flex align-items-center mb-2" style="width:260px;">
<?php if($mode!=='view') : ?>
<button id="openEditorBtn" type="button" class="btn btn-outline-dark btn-sm me-2 viewNoBtn">그리기</button>
<?php else: ?>
<div class="text-muted">
<i class="bi bi-info-circle"></i> 조회 모드, 그리기 기능 불가
</div>
<?php endif; ?>
</div>
<!-- 이미지 표시 영역 (수정된) -->
<div class="card mb-3">
<div class="card-body">
<div class="image-container mb-3" style="position: relative;">
<?php
// bending 테이블의 imgdata 값 사용
$imgUrl = '';
if (!empty($imgdata)) {
// 이미지 파일이 실제로 존재하는지 확인
$imagePath = $_SERVER['DOCUMENT_ROOT'] . '/bending/img/' . $imgdata;
if (file_exists($imagePath)) {
$imgUrl = '/bending/img/' . $imgdata;
} else {
// 기존 경로도 확인
$imagePath2 = $_SERVER['DOCUMENT_ROOT'] . '/bending/img/' . $imgdata;
if (file_exists($imagePath2)) {
$imgUrl = '/bending/img/' . $imgdata;
}
}
}
?>
<?php if (!empty($imgUrl)): ?>
<img
id="targetImage"
src="<?= htmlspecialchars($imgUrl) ?>"
class="img-thumbnail img-fluid"
alt="편집할 이미지"
style="max-width:350px; cursor:pointer; display: block;"
>
<?php else: ?>
<p class="text-muted">이미지가 없습니다.</p>
<?php endif; ?>
<div class="paste-overlay" style="display: none; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); background: rgba(0, 123, 255, 0.8); color: white; padding: 10px 15px; border-radius: 5px; z-index: 1000; text-align: center; font-size: 14px;">
<i class="bi bi-clipboard-plus"></i>
<span>Ctrl+V로 이미지 붙여넣기</span>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</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="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>
<!-- 모듈 스크립트 -->
<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');
// null 체크 추가 (view 모드에서는 openBtn이 없을 수 있음)
if (!imageContainer) {
console.warn('이미지 편집기 초기화 실패: imageContainer를 찾을 수 없습니다.');
return;
}
// view 모드가 아닌 경우에만 openBtn과 targetImg 체크
const mode = '<?= $mode ?>';
if (mode !== 'view') {
if (!openBtn || !targetImg) {
console.warn('이미지 편집기 초기화 실패: 필요한 DOM 요소를 찾을 수 없습니다.');
console.log('openBtn:', openBtn);
console.log('targetImg:', targetImg);
console.log('imageContainer:', imageContainer);
return;
}
} else {
console.log('view 모드: 이미지 편집 기능 비활성화, 붙여넣기만 활성화');
// view 모드에서는 붙여넣기 기능만 활성화
document.addEventListener('paste', handlePaste);
console.log('view 모드에서 전역 붙여넣기 이벤트 리스너 등록됨');
return;
}
// pasteOverlay는 선택적 요소로 처리
const pasteOverlay = document.querySelector('.paste-overlay');
// 붙여넣기 이벤트 처리 함수
function handlePaste(event) {
console.log('붙여넣기 이벤트 발생:', event);
console.log('clipboardData:', event.clipboardData);
const items = (event.clipboardData || event.originalEvent?.clipboardData)?.items;
console.log('clipboard items:', items);
if (!items) {
console.log('클립보드 데이터가 없습니다.');
return;
}
for (let item of items) {
console.log('클립보드 아이템:', item.type);
if (item.type.indexOf('image') !== -1) {
console.log('이미지 발견, 처리 중...');
const blob = item.getAsFile();
const reader = new FileReader();
reader.onload = function(e) {
console.log('이미지 로드 완료');
// view 모드가 아닌 경우에만 targetImg 업데이트
const mode = '<?= $mode ?>';
if (mode !== 'view') {
if (targetImg) {
targetImg.src = e.target.result;
console.log('이미지가 붙여넣기되었습니다:', e.target.result.substring(0, 50) + '...');
}
} else {
console.log('view 모드: 이미지 붙여넣기 완료 (표시만)');
}
// 붙여넣기 성공 피드백
if (pasteOverlay) {
showPasteFeedback(true);
}
};
reader.onerror = function(e) {
console.error('이미지 로드 실패:', e);
};
reader.readAsDataURL(blob);
event.preventDefault();
break;
}
}
}
// 붙여넣기 피드백 표시
function showPasteFeedback(success = false) {
if (!pasteOverlay) {
console.log('pasteOverlay 요소가 없습니다.');
return;
}
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.style.display = 'block';
pasteOverlay.classList.add('show');
setTimeout(() => {
pasteOverlay.classList.remove('show');
setTimeout(() => {
pasteOverlay.style.display = 'none';
}, 300);
}, 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);
let src = targetImg ? targetImg.src : '';
console.log('편집할 이미지 소스:', src);
// 이미지가 없거나 placeholder인 경우 빈 문자열로 설정
if (!src || src.includes('placeholder') || src === 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTAwIiBoZWlnaHQ9IjEwMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cmVjdCB3aWR0aD0iMTAwIiBoZWlnaHQ9IjEwMCIgZmlsbD0iI2Y5ZjlmOSIvPjwvc3ZnPg==') {
console.log('이미지가 없거나 placeholder, 새로운 빈 캔버스로 시작');
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);
if (targetImg) {
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);
console.log('전역 붙여넣기 이벤트 리스너 등록됨');
// 이미지 컨테이너 클릭 시 포커스
imageContainer.addEventListener('click', imageContainerClickHandler);
// 이미지 컨테이너에 포커스 가능하도록 설정
imageContainer.tabIndex = 0;
imageContainer.style.cursor = 'pointer';
imageContainer.title = '클릭 후 Ctrl+V로 이미지 붙여넣기';
// 포커스 시 오버레이 표시
imageContainer.addEventListener('focus', imageContainerFocusHandler);
// 포커스 해제 시 오버레이 숨김
imageContainer.addEventListener('blur', imageContainerBlurHandler);
// 마우스 오버 시 힌트 표시
imageContainer.addEventListener('mouseenter', function() {
if (pasteOverlay) {
pasteOverlay.style.display = 'block';
showPasteFeedback(false);
}
});
imageContainer.addEventListener('mouseleave', function() {
if (pasteOverlay) {
setTimeout(() => {
pasteOverlay.style.display = 'none';
}, 1000);
}
});
// 그리기 버튼 클릭 이벤트 리스너 추가
openBtn.addEventListener('click', openBtnClickHandler);
console.log('이미지 편집기 초기화 완료');
console.log('그리기 버튼 이벤트 리스너 등록됨:', openBtn);
};
</script>
<script>
var ajaxRequest_write =null;
// 전역 변수로 문자입력 모드 관리
let textMode = false;
let isEraser = false; // 이름을 isEraser 로 통일
// 1) calculateSum 수정: 연신율계산 후(calcAfterList[]) 처리 추가
function calculateSum() {
const inputList = $('input[name="inputList[]"]');
const bendingList = $('input[name="bendingrateList[]"]');
const calcAfterList = $('input[name="calcAfterList[]"]');
const sumList = $('input[name="sumList[]"]');
let accumulated = 0;
inputList.each(function(index) {
const inp = parseFloat(inputList.eq(index).val()) || 0;
const bend = parseFloat(bendingList.eq(index).val()) || 0;
// ────────────────────────────────
// 변경: 입력값에서 빼기 대신, bend(±1)를 더합니다.
const diff = inp + bend;
// ────────────────────────────────
calcAfterList.eq(index).val(diff);
accumulated += diff;
sumList.eq(index).val(accumulated);
});
$('#widthsum').val(accumulated);
}
// 2) addInitialColumn 수정
function addInitialColumn() {
const tableBody = $('#tableBody');
if (!tableBody.length) return;
// rowsData 배열에서 span의 className에서 'input-container'를 제거
const rowsData = [
{ label: '번호', type: 'span', className: 'form-control text-center' },
{ label: '입력', type: 'input', className: 'form-control yellowBold text-center', name: 'inputList[]', event: calculateSum },
{ label: '연신율', type: 'input', className: 'form-control text-center', name: 'bendingrateList[]', event: calculateSum },
{ label: '연신율계산 후', type: 'input', className: 'form-control text-center', name: 'calcAfterList[]', event: null, readonly: true },
{ label: '합계', type: 'input', className: 'form-control orangeBlackBold text-center', name: 'sumList[]', event: null },
{ label: '음영', type: 'checkbox', className: 'form-check-input', name: 'colorList[]' },
{ label: 'A각 표시', type: 'checkbox', className: 'form-check-input', name: 'AList[]' }
];
tableBody.empty();
rowsData.forEach((rowData, rowIndex) => {
const $row = $('<tr>');
// 첫 번째 셀: 라벨
$row.append($('<td>').addClass('lightgray').text(rowData.label));
// 두 번째 셀: 모든 행에 input-container 적용
const $cell = $('<td>').addClass('input-container');
if (rowData.type === 'span') {
// 번호용 span: 모든 요소와 동일한 폭 적용
$cell.append(
$('<span>')
.addClass(rowData.className)
.css({ display: 'inline-block', width: '40px', height: '24px', 'text-align': 'center', 'line-height': '24px' })
.text(rowIndex + 1)
);
} else {
// 모든 input 요소: 동일한 폭과 높이 적용
const $input = $('<input>')
.attr('type', rowData.type === 'checkbox' ? 'checkbox' : 'text')
.addClass(rowData.className)
.css(rowData.type === 'checkbox' ? { width: '16px', height: '16px' } : { width: '40px', height: '24px' })
.attr('name', rowData.name)
.prop('readonly', !!rowData.readonly)
.on(rowData.event ? 'input change' : [], rowData.event || (() => {}))
.attr('autocomplete', 'off');
$cell.append($input);
}
$row.append($cell);
tableBody.append($row);
wrapColumnsAndAttachRemove();
});
}
// 3) addColumn 수정: 새로 생긴 "연신율계산 후" 행에도 input 추가
function addColumn() {
const tableBody = $('#tableBody');
let $rows = tableBody.find('tr');
if ($rows.length === 0) {
addInitialColumn();
$rows = tableBody.find('tr');
}
// 새 칸 번호
const newIndex = $rows.first().find('.input-container span').length + 1;
$rows.each(function(rowIdx) {
const $cell = $(this).find('.input-container');
if (rowIdx === 0) {
// 번호 행: 동일한 폭 적용
$cell.append(
$('<span>')
.addClass('form-control text-center viewNoBtn')
.css({ width: '40px', height: '24px', 'text-align': 'center', 'line-height': '24px' })
.text(newIndex)
);
} else {
let $input = $('<input>')
.addClass('form-control')
.attr('autocomplete', 'off');
switch (rowIdx) {
case 1: // 입력
$input
.attr('type', 'text')
.addClass('yellowBold text-center')
.css({ width: '40px', height: '24px' })
.attr('name', 'inputList[]')
.on('input change', calculateSum);
break;
case 2: // 연신율
$input
.attr('type', 'text')
.addClass('text-center')
.css({ width: '40px', height: '24px' })
.attr('name', 'bendingrateList[]')
.on('input change', calculateSum);
break;
case 3: // 연신율계산 후
$input
.attr('type', 'text')
.addClass('text-center')
.css({ width: '40px', height: '24px' })
.attr('name', 'calcAfterList[]')
.prop('readonly', true);
break;
case 4: // 합계
$input
.attr('type', 'text')
.addClass('orangeBlackBold text-center')
.css({ width: '40px', height: '24px' })
.attr('name', 'sumList[]');
break;
case 5: // 음영
$input
.attr('type', 'checkbox')
.addClass('form-check-input')
.css({ width: '16px', height: '16px' })
.attr('name', 'colorList[]');
break;
case 6: // A각 표시
$input
.attr('type', 'checkbox')
.addClass('form-check-input')
.css({ width: '16px', height: '16px' })
.attr('name', 'AList[]');
break;
}
$cell.append($input);
}
wrapColumnsAndAttachRemove();
});
}
// 특정 위치에 열 삽입하는 함수
function insertColumnAt(index) {
const tableBody = $('#tableBody');
let $rows = tableBody.find('tr');
if ($rows.length === 0) {
addInitialColumn();
$rows = tableBody.find('tr');
}
$rows.each(function(rowIdx) {
const $cell = $(this).find('.input-container');
const $colCells = $cell.find('.col-cell');
if (rowIdx === 0) {
// 번호 행: span 요소 삽입
const $newSpan = $('<span>')
.addClass('form-control text-center viewNoBtn')
.css({ width: '40px', height: '24px', 'text-align': 'center', 'line-height': '24px' })
.text(index + 1);
// 해당 위치에 삽입
if (index < $colCells.length) {
$colCells.eq(index).before($('<div class="col-cell"></div>').append($newSpan));
} else {
$cell.append($('<div class="col-cell"></div>').append($newSpan));
}
} else {
let $input = $('<input>')
.addClass('form-control')
.attr('autocomplete', 'off');
switch (rowIdx) {
case 1: // 입력
$input
.attr('type', 'text')
.addClass('yellowBold text-center')
.css({ width: '40px', height: '24px' })
.attr('name', 'inputList[]')
.on('input change', calculateSum);
break;
case 2: // 연신율
$input
.attr('type', 'text')
.addClass('text-center')
.css({ width: '40px', height: '24px' })
.attr('name', 'bendingrateList[]')
.on('input change', calculateSum);
break;
case 3: // 연신율계산 후
$input
.attr('type', 'text')
.addClass('text-center')
.css({ width: '40px', height: '24px' })
.attr('name', 'calcAfterList[]')
.prop('readonly', true);
break;
case 4: // 합계
$input
.attr('type', 'text')
.addClass('orangeBlackBold text-center')
.css({ width: '40px', height: '24px' })
.attr('name', 'sumList[]');
break;
case 5: // 음영
$input
.attr('type', 'checkbox')
.addClass('form-check-input')
.css({ width: '16px', height: '16px' })
.attr('name', 'colorList[]');
break;
case 6: // A각 표시
$input
.attr('type', 'checkbox')
.addClass('form-check-input')
.css({ width: '16px', height: '16px' })
.attr('name', 'AList[]');
break;
}
// 해당 위치에 삽입
if (index < $colCells.length) {
$colCells.eq(index).before($('<div class="col-cell"></div>').append($input));
} else {
$cell.append($('<div class="col-cell"></div>').append($input));
}
}
});
// 새로 추가된 열에 버튼들 추가
addButtonsToNewColumns();
// 번호 다시 매기기
$('#tableBody tr:first .col-cell span').each(function(i) {
$(this).text(i + 1);
});
// 합계 재계산
calculateSum();
alertToast('앞쪽에 열추가');
}
// 새로 추가된 열에 버튼들을 추가하는 함수
function addButtonsToNewColumns() {
$('#tableBody tr:first .col-cell').each(function(idx) {
const $colCell = $(this);
// 추가 버튼이 없으면 추가
if (!$colCell.find('.add-col').length) {
$colCell.append('<button type="button" class="add-col">+</button>');
}
// 삭제 버튼이 없으면 추가
if (!$colCell.find('.remove-col').length) {
$colCell.append('<button type="button" class="remove-col"></button>');
}
});
}
function emptyColumn() {
const tableBody = $('#tableBody');
tableBody.find('input').each(function() {
const inputType = $(this).attr('type');
if (inputType === 'checkbox') {
$(this).prop('checked', false); // 체크박스 해제
} else {
$(this).val(''); // 텍스트 필드 비우기
}
});
}
function populateTableWithData() {
const inputList = <?php echo json_encode($inputList ?? []); ?>;
const bendingrateList = <?php echo json_encode($bendingrateList ?? []); ?>;
const sumList = <?php echo json_encode($sumList ?? []); ?>;
const colorList = <?php echo json_encode($colorList ?? []); ?>;
const AList = <?php echo json_encode($AList ?? []); ?>;
const tableBody = $('#tableBody');
if (!tableBody.length) return;
inputList.forEach(function (input, index) {
if (index === 0) {
addInitialColumn(); // 첫 번째 열은 기본 열 추가
} else {
addColumn(); // 이후 열 추가
}
});
// 데이터 채우기
inputList.forEach(function (input, index) {
// 입력 추가
tableBody.find('tr:nth-child(2) .input-container input').eq(index).val(input);
// 연신율 추가
tableBody.find('tr:nth-child(3) .input-container input').eq(index).val(bendingrateList[index] || '');
// 합계 추가
tableBody.find('tr:nth-child(5) .input-container input').eq(index).val(sumList[index] || '');
// 음영 체크박스 추가
tableBody.find('tr:nth-child(6) .input-container input').eq(index).prop('checked', colorList[index] || false);
// A각 표시 체크박스 추가
tableBody.find('tr:nth-child(7) .input-container input').eq(index).prop('checked', AList[index] || false);
});
}
function removeColumn() {
const tableBody = $('#tableBody');
const rows = tableBody.find('tr');
if (rows.length === 0) return;
rows.each(function (i) {
const inputContainer = $(this).find('.input-container');
if (i === 0) {
// 첫 번째 행(번호 행)은 span 요소를 제거합니다.
const spans = inputContainer.find('span');
if (spans.length > 1) {
inputContainer.find('span').last().remove();
}
} else {
// 나머지 행은 input 요소를 제거합니다.
const inputs = inputContainer.find('input');
if (inputs.length > 1) {
inputContainer.find('input').last().remove();
}
}
});
}
$(document).ready(function () {
const mode = '<?php echo $mode; ?>';
$("#copyBtn").click(function(){
location.href = 'write_form.php?mode=copy&num=' + $("#num").val() + "&tablename=" + $("#tablename").val() ;
}); // end of function
if (mode === 'view' || mode === 'modify' || mode === 'copy' || mode === 'write' ) {
populateTableWithData();
} else {
addInitialColumn(); // 새로 입력하는 경우 기본 열 추가
}
if (mode === 'view') {
disableInputsForViewMode();
}
else
{ // view가 아닐때만
$('#emptyColumnBtn').on('click', emptyColumn);
$('#addColumnBtn').on('click', addColumn);
$('#removeColumnBtn').on('click', removeColumn);
}
var loader = $('#loadingOverlay');
if (loader.length) {
loader.hide();
}
});
function disableInputsForViewMode() {
$('input, textarea').prop('readonly', true);
$('select').prop('disabled', true);
$('input[type=file]').prop('readonly', false);
$('input[type=checkbox]').prop('disabled', true);
$('.viewNoBtn').prop('disabled', true); // 뷰모드에서는 금지
$('input[type=radio]').prop('disabled', true);
}
$(document).ready(function() {
// Log 파일보기
$("#showlogBtn").click( function() {
var num = '<?php echo $num; ?>'
// table 이름을 넣어야 함
var workitem = 'bending' ;
// 버튼 비활성화
var btn = $(this);
popupCenter("../Showlog.php?num=" + num + "&workitem=" + workitem , '로그기록', 500, 500);
btn.prop('disabled', false);
});
// 저장 버튼 클릭 이벤트
$('#saveBtn').off('click').on('click', function() {
// 이미지 편집이 완료되었지만 저장되지 않은 상태인지 확인
const targetImage = document.getElementById('targetImage');
const hasUnsavedImage = targetImage && targetImage.src && targetImage.src.startsWith('data:');
if (hasUnsavedImage) {
Swal.fire({
title: '이미지 편집 저장 확인',
text: '편집된 이미지가 저장되지 않았습니다. 저장하시겠습니까?',
icon: 'question',
showCancelButton: true,
confirmButtonColor: '#3085d6',
cancelButtonColor: '#6c757d',
confirmButtonText: '네, 저장하겠습니다',
cancelButtonText: '아니오, 그대로 저장'
}).then((result) => {
if (result.isConfirmed) {
// 이미지 저장 후 파일 저장
saveEditedImageAndFile();
} else {
// 이미지 저장 없이 파일 저장
saveFileOnly();
}
});
} else {
// 이미지 편집이 없으면 바로 파일 저장
saveFileOnly();
}
});
// 편집된 이미지 저장 후 파일 저장하는 함수
async function saveEditedImageAndFile() {
const targetImage = document.getElementById('targetImage');
if (targetImage && targetImage.src && targetImage.src.startsWith('data:')) {
try {
// 이미지 저장
await saveEditedImage(targetImage.src);
// 파일 저장 진행
saveFileOnly();
} catch (error) {
console.error('이미지 저장 실패:', error);
// 이미지 저장 실패해도 파일 저장은 진행
saveFileOnly();
}
} else {
// 저장할 이미지가 없으면 바로 파일 저장
saveFileOnly();
}
}
// 파일만 저장하는 함수
function saveFileOnly() {
const formData = new FormData($('#board_form')[0]);
// JSON 데이터 생성
const inputList = [];
$('input[name="inputList[]"]').each(function() {
inputList.push($(this).val());
});
const bendingrateList = [];
$('input[name="bendingrateList[]"]').each(function() {
bendingrateList.push($(this).val());
});
const sumList = [];
$('input[name="sumList[]"]').each(function() {
sumList.push($(this).val());
});
const colorList = [];
$('input[name="colorList[]"]').each(function() {
colorList.push($(this).is(':checked'));
});
const AList = [];
$('input[name="AList[]"]').each(function() {
AList.push($(this).is(':checked'));
});
const calcAfterList = [];
$('input[name="calcAfterList[]"]').each(function() {
calcAfterList.push($(this).val());
});
// JSON 데이터를 hidden input에 설정
formData.append('inputList', JSON.stringify(inputList));
formData.append('bendingrateList', JSON.stringify(bendingrateList));
formData.append('sumList', JSON.stringify(sumList));
formData.append('colorList', JSON.stringify(colorList));
formData.append('AList', JSON.stringify(AList));
formData.append('calcAfterList', JSON.stringify(calcAfterList));
// AJAX로 저장
$.ajax({
url: 'insert.php',
type: 'POST',
data: formData,
processData: false,
contentType: false,
dataType: 'json',
success: function(response) {
console.log(' saveBtn 실행 저장 응답:', response);
try {
// 응답이 문자열인 경우 JSON 파싱 시도
let result;
if (typeof response === 'string') {
result = JSON.parse(response);
} else {
result = response;
}
if (result && result.success) {
Swal.fire({
title: '저장 완료',
text: '데이터가 성공적으로 저장되었습니다.',
icon: 'success',
confirmButtonText: '확인'
}).then((result) => {
// 저장 성공 시 페이지 새로고침 또는 다른 처리
if (result.num) {
location.href = 'write_form.php?mode=view&num=' + result.num + '&tablename=' + $('#tablename').val();
} else {
// num이 없으면 현재 페이지 새로고침
// location.reload();
}
});
} else {
Swal.fire({
title: '저장 실패',
text: '저장 중 오류가 발생했습니다: ' + (result ? result.message : '알 수 없는 오류'),
icon: 'error',
confirmButtonText: '확인'
});
}
} catch (e) {
console.error('저장 응답 파싱 오류:', e);
console.error('응답 내용:', response);
// JSON 파싱 실패해도 성공으로 처리 (insert.php가 성공 시 다른 형식 반환할 수 있음)
Swal.fire({
title: '저장 완료',
text: '데이터가 성공적으로 저장되었습니다.',
icon: 'success',
confirmButtonText: '확인'
}).then(() => {
location.reload();
});
}
},
error: function(xhr, status, error) {
Swal.fire({
title: '저장 오류',
text: '저장 중 오류가 발생했습니다.',
icon: 'error',
confirmButtonText: '확인'
});
console.error('저장 오류:', error);
}
});
}
// saveEditedImage 함수 (모듈 스크립트에서 정의된 함수 사용)
async function saveEditedImage(imageData) {
try {
console.log('이미지 저장 시작:', imageData.substring(0, 50) + '...');
// data URL을 Blob으로 변환
const response = await fetch(imageData);
const blob = await response.blob();
// FormData 구성
const formData = new FormData();
formData.append('upfile', blob, 'edited_image.png');
formData.append('num', '<?= $num ?>');
formData.append('mode', '<?= $mode ?>');
// 추가 데이터 (bending 테이블 관련)
formData.append('item_sep', '<?= $row['item_sep'] ?? '' ?>');
formData.append('model_UA', '<?= $row['model_UA'] ?? '' ?>');
formData.append('item_bending', '<?= $row['item_bending'] ?? '' ?>');
formData.append('itemName', '<?= $row['itemName'] ?? '' ?>');
formData.append('item_spec', '<?= $row['item_spec'] ?? '' ?>');
console.log('FormData 구성 완료, 서버 전송 중...');
const saveResponse = await fetch('save_edited_image.php', {
method: 'POST',
body: formData
});
if (!saveResponse.ok) {
throw new Error('서버 저장 실패');
}
const result = await saveResponse.json();
if (result.success) {
console.log('이미지 저장 성공:', result);
// 성공 메시지 표시
if (typeof Toastify !== 'undefined') {
Toastify({
text: "이미지 저장 완료",
duration: 2000,
close: true,
gravity: "top",
position: "center",
backgroundColor: "#28a745",
}).showToast();
} else {
alert('이미지가 성공적으로 저장되었습니다.');
}
} else {
console.error('이미지 저장 실패:', result.message);
if (typeof Toastify !== 'undefined') {
Toastify({
text: "이미지 저장 실패: " + result.message,
duration: 3000,
close: true,
gravity: "top",
position: "center",
backgroundColor: "#dc3545",
}).showToast();
} else {
alert('이미지 저장 실패: ' + result.message);
}
}
} catch (error) {
console.error('이미지 저장 오류:', error);
if (typeof Toastify !== 'undefined') {
Toastify({
text: "이미지 저장 중 오류 발생",
duration: 3000,
close: true,
gravity: "top",
position: "center",
backgroundColor: "#dc3545",
}).showToast();
} else {
alert('이미지 저장 중 오류가 발생했습니다.');
}
}
}
});
// 테이블을 다시 그린 직후, 또는 addColumn/addInitialColumn 끝에 호출하세요.
function wrapColumnsAndAttachRemove() {
$('#tableBody 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>');
});
});
// 모든 열에 추가/삭제 버튼 붙이기
addButtonsToNewColumns();
}
// 열추가동작
$(document).on('click', '.add-col', function() {
const $wrapper = $(this).closest('.col-cell');
const idx = $wrapper.index(); // 몇 번째 열인지
// mode가 view가 아닐때 해당 열(인덱스) 위치에 새 열 삽입
if ($('#mode').val() !== 'view') {
insertColumnAt(idx);
}
});
// 열삭제동작
$(document).on('click', '.remove-col', function() {
const $wrapper = $(this).closest('.col-cell');
const idx = $wrapper.index(); // 몇 번째 열인지
// mode가 view가 아닐때 해당 열(인덱스) 전체를 지움
if ($('#mode').val() !== 'view') {
$('#tableBody .input-container').each(function() {
$(this).find('.col-cell').eq(idx).remove();
});
// 번호 다시 매기기
$('#tableBody tr:first .col-cell span').each(function(i) {
$(this).text(i + 1);
});
// 합계 재계산
calculateSum();
alertToast('해당열 열삭제');
}
});
</script>
<script>
// 이미지 편집 관련 전역 변수 선언
$(document).ready(function() {
// 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;
}
// 현재 이미지 요소 찾기 (기존 targetImg 또는 새로 생성된 이미지)
let currentImg = targetImg || imageContainer.querySelector('#targetImage');
let src = currentImg ? currentImg.src : '';
console.log('편집할 이미지 소스:', src);
// 이미지가 없거나 placeholder인 경우 빈 문자열로 설정
if (!src || src.includes('placeholder') || src === 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTAwIiBoZWlnaHQ9IjEwMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cmVjdCB3aWR0aD0iMTAwIiBoZWlnaHQ9IjEwMCIgZmlsbD0iI2Y5ZjlmOSIvPjwvc3ZnPg==') {
console.log('이미지가 없거나 placeholder, 새로운 빈 캔버스로 시작');
src = '';
}
try {
const result = globalOpenImageEditor(src);
console.log('openImageEditor 호출 결과:', result);
if (result && typeof result.then === 'function') {
result.then(newUrl => {
console.log('편집 완료, 새로운 이미지 URL:', newUrl);
// 현재 이미지 요소 찾기 (기존 targetImg 또는 새로 생성된 이미지)
let imgElement = targetImg || imageContainer.querySelector('#targetImage');
if (!imgElement) {
console.log('이미지 요소가 없어서 새로 생성');
imgElement = document.createElement('img');
imgElement.id = 'targetImage';
imgElement.className = 'img-fluid';
imgElement.style.maxWidth = '100%';
imgElement.style.height = 'auto';
imageContainer.appendChild(imgElement);
}
imgElement.src = newUrl;
// "이미지가 없습니다" 메시지 숨기기
const noImageMsg = imageContainer.querySelector('p.text-muted');
if (noImageMsg && noImageMsg.textContent.includes('이미지가 없습니다')) {
noImageMsg.style.display = 'none';
console.log('"이미지가 없습니다" 메시지 숨김');
}
}).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('붙여넣기 오버레이 찾음');
// 붙여넣기 기능 초기화 (imageContainer만 있으면 동작)
if (imageContainer) {
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가 있으면 업데이트, 없으면 새로 생성
let imgElement = targetImg;
if (!imgElement) {
console.log('targetImg가 없어서 새로 생성');
imgElement = document.createElement('img');
imgElement.id = 'targetImage';
imgElement.className = 'img-fluid';
imgElement.style.maxWidth = '100%';
imgElement.style.height = 'auto';
imageContainer.appendChild(imgElement);
}
imgElement.src = e.target.result;
console.log('이미지가 붙여넣기되었습니다:', e.target.result);
// "이미지가 없습니다" 메시지 숨기기
const noImageMsg = imageContainer.querySelector('p.text-muted');
if (noImageMsg && noImageMsg.textContent.includes('이미지가 없습니다')) {
noImageMsg.style.display = 'none';
console.log('"이미지가 없습니다" 메시지 숨김');
}
// 붙여넣기 성공 피드백
showPasteFeedback(true);
};
reader.readAsDataURL(blob);
event.preventDefault();
break;
}
}
}
// 붙여넣기 피드백 표시
function showPasteFeedback(success = false) {
if (pasteOverlay) {
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);
} else {
// pasteOverlay가 없으면 간단한 콘솔 메시지
if (success) {
console.log('이미지가 성공적으로 붙여넣기되었습니다!');
}
}
}
// 이벤트 핸들러 함수들
function imageContainerClickHandler() {
imageContainer.focus();
}
function imageContainerFocusHandler() {
showPasteFeedback();
}
function imageContainerBlurHandler() {
if (pasteOverlay) {
pasteOverlay.classList.remove('show');
}
}
// 기존 이벤트 리스너 제거 (중복 방지)
document.removeEventListener('paste', handlePaste);
imageContainer.removeEventListener('click', imageContainerClickHandler);
if (pasteOverlay) {
imageContainer.removeEventListener('focus', imageContainerFocusHandler);
imageContainer.removeEventListener('blur', imageContainerBlurHandler);
}
// 붙여넣기 이벤트 리스너 추가
document.addEventListener('paste', handlePaste);
// 이미지 컨테이너 클릭 시 포커스
imageContainer.addEventListener('click', imageContainerClickHandler);
// 이미지 컨테이너에 포커스 가능하도록 설정
imageContainer.tabIndex = 0;
imageContainer.title = '클릭 후 Ctrl+V로 이미지 붙여넣기';
// 포커스 시 오버레이 표시 (pasteOverlay가 있을 때만)
if (pasteOverlay) {
imageContainer.addEventListener('focus', imageContainerFocusHandler);
imageContainer.addEventListener('blur', imageContainerBlurHandler);
}
console.log('붙여넣기 기능 초기화 완료');
} else {
console.warn('붙여넣기 기능 초기화 실패: 필요한 DOM 요소를 찾을 수 없습니다.');
}
}, 100);
});
</script>
</body>
</html>