초기 커밋: 5130 레거시 시스템
- URL 하드코딩 → .env APP_URL 기반 동적 URL로 변경 - DB 연결 하드코딩 → .env 기반으로 변경 - MySQL strict mode DATE 오류 수정
This commit is contained in:
143
modelsTree/copyCategory.php
Normal file
143
modelsTree/copyCategory.php
Normal file
@@ -0,0 +1,143 @@
|
||||
<?php
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
require_once($_SERVER['DOCUMENT_ROOT'].'/session.php');
|
||||
require_once($_SERVER['DOCUMENT_ROOT'].'/lib/mydb.php');
|
||||
$pdo = db_connect();
|
||||
|
||||
// GET 파라미터 level, id
|
||||
$level = isset($_GET['level']) ? intval($_GET['level']) : 0;
|
||||
$id = isset($_GET['id']) ? intval($_GET['id']) : 0;
|
||||
|
||||
// 여기선 level=1 만 처리한다고 가정
|
||||
if($level !== 1) {
|
||||
echo json_encode(["result"=>"error","msg"=>"Only level=1 copy is implemented"]);
|
||||
exit;
|
||||
}
|
||||
|
||||
// 트랜잭션 시작 (복사 도중 에러나면 rollback)
|
||||
$pdo->beginTransaction();
|
||||
try {
|
||||
// 1) 원본 L1 가져오기
|
||||
$sql = "SELECT * FROM {$DB}.category_l1 WHERE id = :id";
|
||||
$st = $pdo->prepare($sql);
|
||||
$st->bindValue(':id', $id, PDO::PARAM_INT);
|
||||
$st->execute();
|
||||
$row_l1 = $st->fetch(PDO::FETCH_ASSOC);
|
||||
if(!$row_l1) {
|
||||
throw new Exception("원본 L1이 존재하지 않습니다.");
|
||||
}
|
||||
|
||||
// 2) 새 L1 생성
|
||||
// 예: 이름에 (복사) 접미사 추가
|
||||
$newName = $row_l1['name'] . " (복사)";
|
||||
$sql = "INSERT INTO {$DB}.category_l1 (name, sortOrder)
|
||||
VALUES (:name, :sortOrder)";
|
||||
$st = $pdo->prepare($sql);
|
||||
$st->bindValue(':name', $newName);
|
||||
$st->bindValue(':sortOrder', $row_l1['sortOrder'] ?? 0);
|
||||
$st->execute();
|
||||
$new_l1_id = $pdo->lastInsertId(); // 새 L1의 ID
|
||||
|
||||
// 3) 원본 L2 목록 가져오기
|
||||
$sql = "SELECT * FROM {$DB}.category_l2 WHERE parent_id = :pid";
|
||||
$st = $pdo->prepare($sql);
|
||||
$st->bindValue(':pid', $id, PDO::PARAM_INT);
|
||||
$st->execute();
|
||||
$rows_l2 = $st->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
// "구(old)->신(new)" ID 매핑 테이블
|
||||
$l2_map_id = [];
|
||||
|
||||
// 4) L2들 복사
|
||||
foreach($rows_l2 as $l2){
|
||||
// 새 L2 Insert
|
||||
$sql = "INSERT INTO {$DB}.category_l2 (name, parent_id, sortOrder)
|
||||
VALUES (:name, :p, :so)";
|
||||
$st2 = $pdo->prepare($sql);
|
||||
// 이름에 (복사) 붙일지 여부 => 선택
|
||||
$copyName = $l2['name'] ;
|
||||
$st2->bindValue(':name', $copyName);
|
||||
$st2->bindValue(':p', $new_l1_id, PDO::PARAM_INT);
|
||||
$st2->bindValue(':so', $l2['sortOrder'] ?? 0, PDO::PARAM_INT);
|
||||
$st2->execute();
|
||||
$new_l2_id = $pdo->lastInsertId();
|
||||
|
||||
// 구 -> 신 매핑
|
||||
$old_l2_id = $l2['id'];
|
||||
$l2_map_id[$old_l2_id] = $new_l2_id;
|
||||
}
|
||||
|
||||
// 5) 원본 L3 전부 가져오기 (조건: parent_id in (원본 l2 ids))
|
||||
// 혹은 l2를 돌면서 l3를 가져와도 됨
|
||||
if(!empty($l2_map_id)){
|
||||
$old_l2_ids_str = implode(',', array_map('intval', array_keys($l2_map_id)));
|
||||
$sql = "SELECT * FROM {$DB}.category_l3
|
||||
WHERE parent_id IN ($old_l2_ids_str)";
|
||||
$st = $pdo->prepare($sql);
|
||||
$st->execute();
|
||||
$rows_l3 = $st->fetchAll(PDO::FETCH_ASSOC);
|
||||
} else {
|
||||
$rows_l3 = [];
|
||||
}
|
||||
|
||||
$l3_map_id = [];
|
||||
|
||||
// 6) L3 복사
|
||||
foreach($rows_l3 as $l3){
|
||||
$old_parent = $l3['parent_id']; // 원본 l2 id
|
||||
if(!isset($l2_map_id[$old_parent])) continue; // 무효
|
||||
$new_parent = $l2_map_id[$old_parent]; // 새로 생성된 l2 id
|
||||
$copyName = $l3['name'] ;
|
||||
|
||||
$sql = "INSERT INTO {$DB}.category_l3 (name, parent_id, sortOrder)
|
||||
VALUES (:name, :p, :so)";
|
||||
$st3 = $pdo->prepare($sql);
|
||||
$st3->bindValue(':name', $copyName);
|
||||
$st3->bindValue(':p', $new_parent, PDO::PARAM_INT);
|
||||
$st3->bindValue(':so', $l3['sortOrder'] ?? 0, PDO::PARAM_INT);
|
||||
$st3->execute();
|
||||
$new_l3_id = $pdo->lastInsertId();
|
||||
|
||||
$old_l3_id = $l3['id'];
|
||||
$l3_map_id[$old_l3_id] = $new_l3_id;
|
||||
}
|
||||
|
||||
// 7) 원본 L4
|
||||
$l4_map_id = [];
|
||||
if(!empty($l3_map_id)){
|
||||
$old_l3_ids_str = implode(',', array_map('intval', array_keys($l3_map_id)));
|
||||
$sql = "SELECT * FROM {$DB}.category_l4
|
||||
WHERE parent_id IN ($old_l3_ids_str)";
|
||||
$st = $pdo->prepare($sql);
|
||||
$st->execute();
|
||||
$rows_l4 = $st->fetchAll(PDO::FETCH_ASSOC);
|
||||
} else {
|
||||
$rows_l4 = [];
|
||||
}
|
||||
|
||||
// 8) L4 복사
|
||||
foreach($rows_l4 as $l4){
|
||||
$old_parent = $l4['parent_id'];
|
||||
if(!isset($l3_map_id[$old_parent])) continue;
|
||||
$new_parent = $l3_map_id[$old_parent];
|
||||
$copyName = $l4['name'] ;
|
||||
|
||||
$sql = "INSERT INTO {$DB}.category_l4 (name, parent_id, sortOrder)
|
||||
VALUES (:name, :p, :so)";
|
||||
$st4 = $pdo->prepare($sql);
|
||||
$st4->bindValue(':name', $copyName);
|
||||
$st4->bindValue(':p', $new_parent, PDO::PARAM_INT);
|
||||
$st4->bindValue(':so', $l4['sortOrder'] ?? 0, PDO::PARAM_INT);
|
||||
$st4->execute();
|
||||
$new_l4_id = $pdo->lastInsertId();
|
||||
|
||||
$old_l4_id = $l4['id'];
|
||||
$l4_map_id[$old_l4_id] = $new_l4_id;
|
||||
}
|
||||
|
||||
$pdo->commit(); // 전체 복사 성공
|
||||
echo json_encode(["result"=>"ok"]);
|
||||
} catch(Exception $e){
|
||||
$pdo->rollBack();
|
||||
echo json_encode(["result"=>"error","msg"=>$e->getMessage()]);
|
||||
}
|
||||
46
modelsTree/deleteCategory.php
Normal file
46
modelsTree/deleteCategory.php
Normal file
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
require_once($_SERVER['DOCUMENT_ROOT'].'/session.php');
|
||||
require_once($_SERVER['DOCUMENT_ROOT'].'/lib/mydb.php');
|
||||
$pdo = db_connect();
|
||||
|
||||
// GET 파라미터
|
||||
$level = isset($_GET['level']) ? intval($_GET['level']) : 0;
|
||||
$id = isset($_GET['id']) ? intval($_GET['id']) : 0;
|
||||
|
||||
try {
|
||||
// 유효성 검사
|
||||
if ($level < 1 || $level > 4) {
|
||||
throw new Exception("invalid level");
|
||||
}
|
||||
if ($id < 1) {
|
||||
throw new Exception("invalid id");
|
||||
}
|
||||
|
||||
// level에 따라 삭제할 테이블 결정
|
||||
switch($level){
|
||||
case 1:
|
||||
// L1 삭제 → 외래키 CASCADE로 L2/L3/L4도 함께 삭제됨
|
||||
$sql="DELETE FROM {$DB}.category_l1 WHERE id=:id";
|
||||
break;
|
||||
case 2:
|
||||
$sql="DELETE FROM {$DB}.category_l2 WHERE id=:id";
|
||||
break;
|
||||
case 3:
|
||||
$sql="DELETE FROM {$DB}.category_l3 WHERE id=:id";
|
||||
break;
|
||||
case 4:
|
||||
$sql="DELETE FROM {$DB}.category_l4 WHERE id=:id";
|
||||
break;
|
||||
}
|
||||
|
||||
$st = $pdo->prepare($sql);
|
||||
$st->bindValue(':id', $id, PDO::PARAM_INT);
|
||||
$st->execute();
|
||||
|
||||
// 성공 응답
|
||||
echo json_encode(["result" => "ok"]);
|
||||
} catch(Exception $e) {
|
||||
// 오류 응답
|
||||
echo json_encode(["result" => "error", "msg" => $e->getMessage()]);
|
||||
}
|
||||
448
modelsTree/modelsTree.php
Normal file
448
modelsTree/modelsTree.php
Normal file
@@ -0,0 +1,448 @@
|
||||
<?php
|
||||
require_once($_SERVER['DOCUMENT_ROOT'].'/session.php');
|
||||
require_once($_SERVER['DOCUMENT_ROOT'].'/lib/mydb.php');
|
||||
$pdo = db_connect();
|
||||
|
||||
$title_message = '경동기업 4단계 BOM 구성';
|
||||
|
||||
// 1) DB에서 1~4레벨 카테고리 전부 SELECT
|
||||
$l1_data = $pdo->query("SELECT * FROM {$DB}.category_l1 ORDER BY sortOrder, id")->fetchAll(PDO::FETCH_ASSOC);
|
||||
$l2_data = $pdo->query("SELECT * FROM {$DB}.category_l2 ORDER BY sortOrder, id")->fetchAll(PDO::FETCH_ASSOC);
|
||||
$l3_data = $pdo->query("SELECT * FROM {$DB}.category_l3 ORDER BY sortOrder, id")->fetchAll(PDO::FETCH_ASSOC);
|
||||
$l4_data = $pdo->query("SELECT * FROM {$DB}.category_l4 ORDER BY sortOrder, id")->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
// 2) PHP에서 parent_id별로 배열화
|
||||
$l2_map = [];
|
||||
foreach($l2_data as $row2){
|
||||
$l2_map[$row2['parent_id']][] = $row2;
|
||||
}
|
||||
$l3_map = [];
|
||||
foreach($l3_data as $row3){
|
||||
$l3_map[$row3['parent_id']][] = $row3;
|
||||
}
|
||||
$l4_map = [];
|
||||
foreach($l4_data as $row4){
|
||||
$l4_map[$row4['parent_id']][] = $row4;
|
||||
}
|
||||
|
||||
include $_SERVER['DOCUMENT_ROOT'] . '/load_header.php'; // common.php 포함되어 있음
|
||||
|
||||
?>
|
||||
|
||||
<title><?=$title_message?></title>
|
||||
<style>
|
||||
.tree-node {
|
||||
margin-left: 1.25rem; /* Bootstrap의 1rem = 16px -> 약 20px */
|
||||
}
|
||||
.toggle-btn {
|
||||
cursor: pointer;
|
||||
color: #0d6efd; /* 부트스트랩 primary 색상 */
|
||||
margin-right: 0.25rem;
|
||||
}
|
||||
.d-none {
|
||||
display: none !important;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="container mt-4">
|
||||
<div class="card justify-content-center text-center mt-3 mb-2">
|
||||
<div class="card-body">
|
||||
<div class="d-flex justify-content-center align-items-center mb-2">
|
||||
<h4 class="mx-1"><?=$title_message?></h4>
|
||||
<button type="button" class="btn btn-dark btn-sm mx-3" onclick='location.reload();' > <i class="bi bi-arrow-clockwise"></i> </button>
|
||||
<button type="button" class="btn btn-success btn-sm mx-1" onclick="addCategory(1,0)"> <i class="bi bi-plus-square"></i> 최상위 추가 </button>
|
||||
<button type="button" class="btn btn-primary btn-sm mx-1" onclick="expandAllTree();"> <i class="bi bi-plus-square"></i> 전체 펼치기 </button>
|
||||
<button type="button" class="btn btn-secondary btn-sm mx-1" onclick="foldAllTree();"> <i class="bi bi-plus-square"></i> 전체 접기 </button>
|
||||
<button type="button" class="btn btn-dark btn-sm mx-5" onclick="self.close();"> × 닫기 </button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 레벨1 목록 -->
|
||||
<?php foreach($l1_data as $l1): ?>
|
||||
<div class="tree-node mb-2">
|
||||
<!-- 펼치기/접기 아이콘 -->
|
||||
<span class="toggle-btn" data-target="l1-<?= $l1['id'] ?>" >
|
||||
<i class="bi bi-caret-right-fill"></i>
|
||||
</span>
|
||||
<strong><?= htmlspecialchars($l1['name']) ?></strong>
|
||||
|
||||
<!-- 수정/삭제/하위추가 버튼들 -->
|
||||
<!-- 1단계 항목의 복사 버튼 (아이콘: bi-files) -->
|
||||
<button class="btn btn-sm btn-outline-secondary ms-1 mb-1" style="padding:2"
|
||||
onclick="copyCategory(1, <?= $l1['id'] ?>)">
|
||||
<i class="bi bi-files"></i>
|
||||
</button>
|
||||
<button class="btn btn-sm btn-outline-info ms-2 mb-1" style="padding : 2"
|
||||
onclick="editCategory(1, <?= $l1['id'] ?> , '<?= $l1['name'] ?>')">
|
||||
<i class="bi bi-pencil-square"></i>
|
||||
</button>
|
||||
<button class="btn btn-sm btn-outline-danger ms-1 mb-1" style="padding : 2"
|
||||
onclick="deleteCategory(1, <?= $l1['id'] ?>)">
|
||||
<i class="bi bi-trash3"></i>
|
||||
</button>
|
||||
<button class="btn btn-sm btn-outline-primary ms-1 mb-1" style="padding : 2"
|
||||
onclick="addCategory(2, <?= $l1['id'] ?>)">
|
||||
<i class="bi bi-plus-square"></i>
|
||||
</button>
|
||||
|
||||
<!-- L2 목록 -->
|
||||
<div id="l1-<?= $l1['id'] ?>" class="d-none">
|
||||
<?php if(isset($l2_map[$l1['id']])):
|
||||
foreach($l2_map[$l1['id']] as $l2):
|
||||
$l2_id = $l2['id'];
|
||||
?>
|
||||
<div class="tree-node mb-2">
|
||||
|
||||
<span class="toggle-btn" data-target="l2-<?= $l2_id ?>">
|
||||
<i class="bi bi-caret-right-fill"></i>
|
||||
</span>
|
||||
<?= htmlspecialchars($l2['name']) ?>
|
||||
|
||||
<button class="btn btn-sm btn-outline-info ms-2 mb-1" style="padding : 2"
|
||||
onclick="editCategory(2, <?= $l2_id ?>, '<?= $l2['name'] ?>')">
|
||||
<i class="bi bi-pencil-square"></i>
|
||||
</button>
|
||||
<button class="btn btn-sm btn-outline-danger ms-1 mb-1" style="padding : 2"
|
||||
onclick="deleteCategory(2, <?= $l2_id ?>)">
|
||||
<i class="bi bi-trash3"></i>
|
||||
</button>
|
||||
<button class="btn btn-sm btn-outline-primary ms-1 mb-1" style="padding : 2"
|
||||
onclick="addCategory(3, <?= $l2_id ?>)">
|
||||
<i class="bi bi-plus-square"></i>
|
||||
</button>
|
||||
|
||||
<!-- L3 목록 -->
|
||||
<div id="l2-<?= $l2_id ?>" class="d-none">
|
||||
<?php if(isset($l3_map[$l2_id])):
|
||||
foreach($l3_map[$l2_id] as $l3):
|
||||
$l3_id = $l3['id'];
|
||||
?>
|
||||
<div class="tree-node mb-2">
|
||||
|
||||
<span class="toggle-btn" data-target="l3-<?= $l3_id ?>">
|
||||
<i class="bi bi-caret-right-fill"></i>
|
||||
</span>
|
||||
<?= htmlspecialchars($l3['name']) ?>
|
||||
|
||||
<button class="btn btn-sm btn-outline-info ms-2 mb-1" style="padding : 2"
|
||||
onclick="editCategory(3, <?= $l3_id ?>, '<?= $l3['name'] ?>')">
|
||||
<i class="bi bi-pencil-square"></i>
|
||||
</button>
|
||||
<button class="btn btn-sm btn-outline-danger ms-1 mb-1" style="padding : 2"
|
||||
onclick="deleteCategory(3, <?= $l3_id ?>)">
|
||||
<i class="bi bi-trash3"></i>
|
||||
</button>
|
||||
<button class="btn btn-sm btn-outline-primary ms-1 mb-1" style="padding : 2"
|
||||
onclick="addCategory(4, <?= $l3_id ?>)">
|
||||
<i class="bi bi-plus-square"></i>
|
||||
</button>
|
||||
|
||||
<!-- L4 목록 -->
|
||||
<div id="l3-<?= $l3_id ?>" class="d-none">
|
||||
<?php if(isset($l4_map[$l3_id])):
|
||||
foreach($l4_map[$l3_id] as $l4):
|
||||
$l4_id = $l4['id'];
|
||||
?>
|
||||
<div class="tree-node mb-2">
|
||||
|
||||
<?= htmlspecialchars($l4['name']) ?>
|
||||
|
||||
<button class="btn btn-sm btn-outline-info ms-2 mb-1" style="padding : 2"
|
||||
onclick="editCategory(4, <?= $l4_id ?>, '<?= $l4['name'] ?>')">
|
||||
<i class="bi bi-pencil-square"></i>
|
||||
</button>
|
||||
<button class="btn btn-sm btn-outline-danger ms-1 mb-1" style="padding : 2"
|
||||
onclick="deleteCategory(4, <?= $l4_id ?>)">
|
||||
<i class="bi bi-trash3"></i>
|
||||
</button>
|
||||
</div>
|
||||
<?php endforeach; endif; ?>
|
||||
</div>
|
||||
<!-- end L4 -->
|
||||
|
||||
</div>
|
||||
<?php endforeach; endif; ?>
|
||||
</div>
|
||||
<!-- end L3 -->
|
||||
</div>
|
||||
<?php endforeach; endif; ?>
|
||||
</div>
|
||||
<!-- end L2 -->
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Bootstrap Modal -->
|
||||
<div class="modal fade" id="editModal" tabindex="-1" aria-labelledby="editModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="editModalLabel">카테고리 수정/추가</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="닫기"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<input type="hidden" id="catLevel" value="">
|
||||
<input type="hidden" id="catId" value="">
|
||||
<input type="hidden" id="catParent" name="catParent" value="">
|
||||
<div class="mb-3">
|
||||
<label for="catName" class="form-label">이름</label>
|
||||
<input type="text" class="form-control" id="catName" name="catName" placeholder="카테고리명" autocomplete="off">
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-primary mx-1" onclick="saveCategory()">저장</button>
|
||||
<button type="button" class="btn btn-secondary mx-1" data-bs-dismiss="modal">취소</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 로딩 오버레이 숨김 + 트리 토글 + SweetAlert2 알림 -->
|
||||
<script>
|
||||
$(document).ready(function(){
|
||||
// 로딩 hide
|
||||
var loader = document.getElementById('loadingOverlay');
|
||||
if(loader) loader.style.display = 'none';
|
||||
|
||||
// // 트리 토글 (부트스트랩 아이콘 caret 교체)
|
||||
// document.querySelectorAll('.toggle-btn').forEach(btn => {
|
||||
// btn.addEventListener('click', () => {
|
||||
// const targetId = btn.getAttribute('data-target');
|
||||
// const sub = document.getElementById(targetId);
|
||||
// if(sub) {
|
||||
// if(sub.classList.contains('d-none')){
|
||||
// sub.classList.remove('d-none');
|
||||
// btn.innerHTML = '<i class="bi bi-caret-down-fill"></i>';
|
||||
// } else {
|
||||
// sub.classList.add('d-none');
|
||||
// btn.innerHTML = '<i class="bi bi-caret-right-fill"></i>';
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
// });
|
||||
|
||||
// // [추가 요구사항] 초기에 전체 트리를 펼치기
|
||||
// expandAllTree();
|
||||
});
|
||||
|
||||
// 트리를 전부 접는 함수
|
||||
function foldAllTree(){
|
||||
// 모든 .d-none 제거하고, 아이콘을 caret-down-fill 로 변경
|
||||
document.querySelectorAll('[id^="l1-"], [id^="l2-"], [id^="l3-"]').forEach(subDiv => {
|
||||
subDiv.classList.add('d-none');
|
||||
});
|
||||
// toggle-btn 아이콘도 다 down으로
|
||||
document.querySelectorAll('.toggle-btn').forEach(btn => {
|
||||
btn.innerHTML = '<i class="bi bi-caret-right-fill"></i>';
|
||||
});
|
||||
}
|
||||
// 트리를 전부 펼치는 함수
|
||||
function expandAllTree(){
|
||||
// 모든 .d-none 제거하고, 아이콘을 caret-down-fill 로 변경
|
||||
document.querySelectorAll('[id^="l1-"], [id^="l2-"], [id^="l3-"]').forEach(subDiv => {
|
||||
subDiv.classList.remove('d-none');
|
||||
});
|
||||
// toggle-btn 아이콘도 다 down으로
|
||||
document.querySelectorAll('.toggle-btn').forEach(btn => {
|
||||
btn.innerHTML = '<i class="bi bi-caret-down-fill"></i>';
|
||||
});
|
||||
}
|
||||
|
||||
// 추가
|
||||
function addCategory(level, parentId){
|
||||
document.getElementById('catLevel').value = level;
|
||||
document.getElementById('catId').value = 0; // insert
|
||||
document.getElementById('catParent').value = parentId;
|
||||
document.getElementById('catName').value = '';
|
||||
const myModal = new bootstrap.Modal(document.getElementById('editModal'));
|
||||
myModal.show();
|
||||
}
|
||||
|
||||
// 수정
|
||||
function editCategory(level, id, name){
|
||||
document.getElementById('catLevel').value = level;
|
||||
document.getElementById('catId').value = id;
|
||||
document.getElementById('catName').value = name ;
|
||||
const myModal = new bootstrap.Modal(document.getElementById('editModal'));
|
||||
myModal.show();
|
||||
}
|
||||
|
||||
// 삭제
|
||||
function deleteCategory(level, id){
|
||||
Swal.fire({
|
||||
title: '삭제 확인',
|
||||
text: '정말 삭제하시겠습니까?',
|
||||
icon: 'warning',
|
||||
showCancelButton: true,
|
||||
confirmButtonColor: '#3085d6',
|
||||
cancelButtonColor: '#d33',
|
||||
confirmButtonText: '삭제',
|
||||
cancelButtonText: '취소'
|
||||
}).then((result) => {
|
||||
if (result.isConfirmed) {
|
||||
fetch(`deleteCategory.php?level=${level}&id=${id}`)
|
||||
.then(res=>res.json())
|
||||
.then(data=>{
|
||||
if(data.result==='ok'){
|
||||
Swal.fire({
|
||||
icon: 'success',
|
||||
title: '삭제완료',
|
||||
text: '카테고리가 삭제되었습니다.'
|
||||
}).then(()=> location.reload());
|
||||
} else {
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: '삭제실패',
|
||||
text: data.msg
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 저장
|
||||
function saveCategory(){
|
||||
const level = document.getElementById('catLevel').value;
|
||||
const id = document.getElementById('catId').value;
|
||||
const parentId = document.getElementById('catParent').value;
|
||||
const name = document.getElementById('catName').value.trim();
|
||||
|
||||
if(name === ''){
|
||||
Swal.fire({
|
||||
icon: 'warning',
|
||||
title: '입력확인',
|
||||
text: '이름을 입력하세요.'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
fetch('saveCategory.php',{
|
||||
method:'POST',
|
||||
headers:{ 'Content-Type':'application/json' },
|
||||
body: JSON.stringify({ level, id, parentId, name })
|
||||
})
|
||||
.then(r=>r.json())
|
||||
.then(d=>{
|
||||
if(d.result==='ok'){
|
||||
Swal.fire({
|
||||
icon: 'success',
|
||||
title: '저장완료',
|
||||
text: '카테고리 등록/수정되었습니다.'
|
||||
}).then(()=> location.reload());
|
||||
} else {
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: '오류',
|
||||
text: d.msg
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 복사 카테고리
|
||||
function copyCategory(level, id) {
|
||||
Swal.fire({
|
||||
title: '복사 확인',
|
||||
text: '해당 1단계와 그 하위 항목 전체를 복사하시겠습니까?',
|
||||
icon: 'question',
|
||||
showCancelButton: true,
|
||||
confirmButtonColor: '#3085d6',
|
||||
cancelButtonColor: '#d33',
|
||||
confirmButtonText: '복사',
|
||||
cancelButtonText: '취소'
|
||||
}).then((result) => {
|
||||
if (result.isConfirmed) {
|
||||
fetch(`copyCategory.php?level=${level}&id=${id}`)
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
if(data.result==='ok'){
|
||||
Swal.fire({
|
||||
icon: 'success',
|
||||
title: '복사완료',
|
||||
text: '하위 단계까지 복사되었습니다.'
|
||||
}).then(()=> location.reload());
|
||||
} else {
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: '복사실패',
|
||||
text: data.msg
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<script>
|
||||
// [기존] 트리 토글 (부트스트랩 아이콘 caret 교체) + 쿠키 업데이트 기능 추가
|
||||
function updateTreeStateCookie(){
|
||||
let expandedNodes = [];
|
||||
// 모든 토글 버튼을 순회하면서 대상 요소가 보이면 배열에 추가
|
||||
document.querySelectorAll('.toggle-btn').forEach(btn => {
|
||||
const targetId = btn.getAttribute('data-target');
|
||||
const sub = document.getElementById(targetId);
|
||||
if(sub && !sub.classList.contains('d-none')){
|
||||
expandedNodes.push(targetId);
|
||||
}
|
||||
});
|
||||
// 쿠키에 저장 (유효시간 10분)
|
||||
setCookie("treeState", JSON.stringify(expandedNodes), 10);
|
||||
}
|
||||
|
||||
document.querySelectorAll('.toggle-btn').forEach(btn => {
|
||||
btn.addEventListener('click', () => {
|
||||
const targetId = btn.getAttribute('data-target');
|
||||
const sub = document.getElementById(targetId);
|
||||
if(sub) {
|
||||
if(sub.classList.contains('d-none')){
|
||||
sub.classList.remove('d-none');
|
||||
btn.innerHTML = '<i class="bi bi-caret-down-fill"></i>';
|
||||
} else {
|
||||
sub.classList.add('d-none');
|
||||
btn.innerHTML = '<i class="bi bi-caret-right-fill"></i>';
|
||||
}
|
||||
// 토글 동작 후 현재 트리 상태를 쿠키에 저장
|
||||
updateTreeStateCookie();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// 쿠키에서 트리 상태 복원 함수
|
||||
function restoreTreeStateFromCookie(){
|
||||
let cookieVal = getCookie("treeState");
|
||||
if(cookieVal){
|
||||
try{
|
||||
let expandedNodes = JSON.parse(cookieVal);
|
||||
expandedNodes.forEach(targetId => {
|
||||
let sub = document.getElementById(targetId);
|
||||
if(sub && sub.classList.contains('d-none')){
|
||||
sub.classList.remove('d-none');
|
||||
// 해당 토글 버튼의 아이콘도 down으로 변경
|
||||
document.querySelectorAll(`.toggle-btn[data-target="${targetId}"]`).forEach(btn => {
|
||||
btn.innerHTML = '<i class="bi bi-caret-down-fill"></i>';
|
||||
});
|
||||
}
|
||||
});
|
||||
} catch(e){
|
||||
console.error("Error parsing treeState cookie", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 페이지 로드시 트리 상태 복원
|
||||
document.addEventListener("DOMContentLoaded", function(){
|
||||
restoreTreeStateFromCookie();
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
103
modelsTree/saveCategory.php
Normal file
103
modelsTree/saveCategory.php
Normal file
@@ -0,0 +1,103 @@
|
||||
<?php
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
require_once($_SERVER['DOCUMENT_ROOT'].'/session.php');
|
||||
require_once($_SERVER['DOCUMENT_ROOT'].'/lib/mydb.php');
|
||||
$pdo = db_connect();
|
||||
|
||||
// JS에서 POST된 JSON 파싱
|
||||
$data = json_decode(file_get_contents('php://input'), true);
|
||||
|
||||
$level = intval($data['level'] ?? 0);
|
||||
$id = intval($data['id'] ?? 0); // 0이면 Insert, >0이면 Update
|
||||
$parentId = intval($data['parentId'] ?? 0); // 상위 id (Insert 시 사용)
|
||||
$name = trim($data['name'] ?? '');
|
||||
|
||||
// 예외 처리
|
||||
if ($level < 1 || $level > 4) {
|
||||
echo json_encode(["result"=>"error","msg"=>"invalid level"]);
|
||||
exit;
|
||||
}
|
||||
if ($name === '') {
|
||||
echo json_encode(["result"=>"error","msg"=>"name is empty"]);
|
||||
exit;
|
||||
}
|
||||
|
||||
try {
|
||||
switch($level) {
|
||||
case 1: // category_l1
|
||||
if($id === 0) {
|
||||
// INSERT
|
||||
$sql = "INSERT INTO {$DB}.category_l1 (name) VALUES (:name)";
|
||||
$st = $pdo->prepare($sql);
|
||||
$st->bindValue(':name', $name);
|
||||
$st->execute();
|
||||
} else {
|
||||
// UPDATE
|
||||
$sql = "UPDATE {$DB}.category_l1 SET name=:name WHERE id=:id";
|
||||
$st = $pdo->prepare($sql);
|
||||
$st->bindValue(':name', $name);
|
||||
$st->bindValue(':id', $id, PDO::PARAM_INT);
|
||||
$st->execute();
|
||||
}
|
||||
break;
|
||||
|
||||
case 2: // category_l2
|
||||
if($id === 0) {
|
||||
// 새로 추가 → parentId가 category_l1의 id
|
||||
$sql = "INSERT INTO {$DB}.category_l2 (name, parent_id) VALUES (:name, :p)";
|
||||
$st = $pdo->prepare($sql);
|
||||
$st->bindValue(':name', $name);
|
||||
$st->bindValue(':p', $parentId, PDO::PARAM_INT);
|
||||
$st->execute();
|
||||
} else {
|
||||
// 수정
|
||||
$sql = "UPDATE {$DB}.category_l2 SET name=:name WHERE id=:id";
|
||||
$st = $pdo->prepare($sql);
|
||||
$st->bindValue(':name', $name);
|
||||
$st->bindValue(':id', $id, PDO::PARAM_INT);
|
||||
$st->execute();
|
||||
}
|
||||
break;
|
||||
|
||||
case 3: // category_l3
|
||||
if($id === 0) {
|
||||
// parentId는 category_l2의 id
|
||||
$sql = "INSERT INTO {$DB}.category_l3 (name, parent_id) VALUES (:name, :p)";
|
||||
$st = $pdo->prepare($sql);
|
||||
$st->bindValue(':name', $name);
|
||||
$st->bindValue(':p', $parentId, PDO::PARAM_INT);
|
||||
$st->execute();
|
||||
} else {
|
||||
$sql = "UPDATE {$DB}.category_l3 SET name=:name WHERE id=:id";
|
||||
$st = $pdo->prepare($sql);
|
||||
$st->bindValue(':name', $name);
|
||||
$st->bindValue(':id', $id, PDO::PARAM_INT);
|
||||
$st->execute();
|
||||
}
|
||||
break;
|
||||
|
||||
case 4: // category_l4
|
||||
if($id === 0) {
|
||||
// parentId는 category_l3의 id
|
||||
$sql = "INSERT INTO {$DB}.category_l4 (name, parent_id) VALUES (:name, :p)";
|
||||
$st = $pdo->prepare($sql);
|
||||
$st->bindValue(':name', $name);
|
||||
$st->bindValue(':p', $parentId, PDO::PARAM_INT);
|
||||
$st->execute();
|
||||
} else {
|
||||
$sql = "UPDATE {$DB}.category_l4 SET name=:name WHERE id=:id";
|
||||
$st = $pdo->prepare($sql);
|
||||
$st->bindValue(':name', $name);
|
||||
$st->bindValue(':id', $id, PDO::PARAM_INT);
|
||||
$st->execute();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
echo json_encode(["result" => "ok"]);
|
||||
} catch (Exception $e) {
|
||||
echo json_encode([
|
||||
"result" => "error",
|
||||
"msg" => $e->getMessage()
|
||||
]);
|
||||
}
|
||||
Reference in New Issue
Block a user