03a96b6118
- 드래그로 칸 재배치하면 저장 버튼을 검정→빨강(흰 글씨)으로 강조 - 저장 클릭 시 저장되었습니다. 안내창 표시 후 제출 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
236 lines
8.6 KiB
JavaScript
236 lines
8.6 KiB
JavaScript
/* 말레이시아 창고 랙 보기 — 드래그 재배치 + 저장 + 인쇄 미리보기.
|
||
*
|
||
* - 각 .rv-cell 의 data-entries(JSON)가 진실원천. 드래그 드롭으로 두 칸의
|
||
* 구성(entries)을 맞교환(빈 칸이면 이동). 칸 번호(cell)는 그대로.
|
||
* - 저장: 모든 칸의 현재 구성을 rk_cell/rk_sku/rk_upb/rk_box 병렬배열 hidden
|
||
* 으로 만들어 POST /malaysia/rack/save (서버 reposition_rack).
|
||
* - 인쇄: 체크한 칸을 칸마다 A4 가로 1장으로 미리보기 후 window.print().
|
||
* - 언어: localStorage('mys_lang'). 셀/미리보기 텍스트를 현재 언어로 렌더.
|
||
*/
|
||
(function () {
|
||
"use strict";
|
||
|
||
function lang() { return localStorage.getItem("mys_lang") === "en" ? "en" : "ko"; }
|
||
function t(ko, en) { return lang() === "en" ? en : ko; }
|
||
function fmt(n) { return Number(n || 0).toLocaleString(); }
|
||
function nameOf(e) { return (lang() === "en" ? e.en : e.ko) || e.sku; }
|
||
// 상품명 끝의 용량 토큰(숫자 포함, 예 750ml)을 분리 → {main, size}
|
||
function splitName(full) {
|
||
var m = String(full || "").match(/^(.*\S)\s+(\S*\d\S*)$/);
|
||
if (m) return { main: m[1], size: m[2] };
|
||
return { main: full || "", size: "" };
|
||
}
|
||
|
||
function getEntries(cell) {
|
||
try { return JSON.parse(cell.getAttribute("data-entries") || "[]"); }
|
||
catch (_) { return []; }
|
||
}
|
||
function setEntries(cell, arr) {
|
||
cell.setAttribute("data-entries", JSON.stringify(arr));
|
||
}
|
||
|
||
function renderCell(cell) {
|
||
var items = cell.querySelector(".rv-items");
|
||
if (!items) return;
|
||
var arr = getEntries(cell);
|
||
items.innerHTML = "";
|
||
if (!arr.length) {
|
||
cell.classList.add("is-empty");
|
||
var s = document.createElement("span");
|
||
s.className = "rv-empty-note";
|
||
s.textContent = "—";
|
||
items.appendChild(s);
|
||
return;
|
||
}
|
||
cell.classList.remove("is-empty");
|
||
arr.forEach(function (e) {
|
||
var total = (Number(e.upb) || 0) * (Number(e.box) || 0);
|
||
var wrap = document.createElement("div");
|
||
wrap.className = "rv-item";
|
||
var nm = document.createElement("span");
|
||
nm.className = "rv-name";
|
||
nm.textContent = nameOf(e);
|
||
var calc = document.createElement("span");
|
||
calc.className = "rv-calc";
|
||
calc.innerHTML = fmt(e.upb) + t("개", "ea") + " × " + fmt(e.box) + t("박스", "box")
|
||
+ " = <span class=\"rv-qty\">" + fmt(total) + "</span>";
|
||
wrap.appendChild(nm);
|
||
wrap.appendChild(document.createElement("br"));
|
||
wrap.appendChild(calc);
|
||
items.appendChild(wrap);
|
||
});
|
||
}
|
||
function renderAll() {
|
||
document.querySelectorAll(".rv-cell").forEach(renderCell);
|
||
}
|
||
|
||
// ── 드래그 재배치 ──
|
||
var dragSrc = null;
|
||
var dirty = false;
|
||
function markDirty() {
|
||
dirty = true;
|
||
var b = document.getElementById("rv-save-btn");
|
||
if (b) b.classList.add("is-dirty");
|
||
}
|
||
function cleanup() {
|
||
if (dragSrc) dragSrc.classList.remove("dragging");
|
||
dragSrc = null;
|
||
document.querySelectorAll(".rv-cell.is-dragover")
|
||
.forEach(function (c) { c.classList.remove("is-dragover"); });
|
||
}
|
||
function onDragStart(e) {
|
||
var cell = e.target.closest(".rv-cell");
|
||
if (!cell) return;
|
||
dragSrc = cell;
|
||
cell.classList.add("dragging");
|
||
e.dataTransfer.effectAllowed = "move";
|
||
try { e.dataTransfer.setData("text/plain", cell.getAttribute("data-cell")); } catch (_) {}
|
||
}
|
||
function onDragOver(e) {
|
||
if (!dragSrc) return;
|
||
e.preventDefault();
|
||
e.dataTransfer.dropEffect = "move";
|
||
var cell = e.target.closest(".rv-cell");
|
||
if (cell && cell !== dragSrc) cell.classList.add("is-dragover");
|
||
}
|
||
function onDragLeave(e) {
|
||
var cell = e.target.closest(".rv-cell");
|
||
if (cell) cell.classList.remove("is-dragover");
|
||
}
|
||
function onDrop(e) {
|
||
e.preventDefault();
|
||
var dst = e.target.closest(".rv-cell");
|
||
if (!dst || !dragSrc || dst === dragSrc) { cleanup(); return; }
|
||
var a = getEntries(dragSrc);
|
||
var b = getEntries(dst);
|
||
setEntries(dragSrc, b);
|
||
setEntries(dst, a);
|
||
renderCell(dragSrc);
|
||
renderCell(dst);
|
||
markDirty();
|
||
cleanup();
|
||
}
|
||
|
||
// ── 인쇄 선택(체크박스) → 셀 강조 ──
|
||
function onChange(e) {
|
||
var cb = e.target;
|
||
if (!cb.classList || !cb.classList.contains("rv-pick")) return;
|
||
var cell = cb.closest(".rv-cell");
|
||
if (cell) cell.classList.toggle("is-selected", cb.checked);
|
||
}
|
||
|
||
// ── 저장 ──
|
||
function addHidden(parent, name, val) {
|
||
var i = document.createElement("input");
|
||
i.type = "hidden";
|
||
i.name = name;
|
||
i.value = val;
|
||
parent.appendChild(i);
|
||
}
|
||
function save() {
|
||
var fields = document.getElementById("rv-save-fields");
|
||
var form = document.getElementById("rv-save-form");
|
||
if (!fields || !form) return;
|
||
fields.innerHTML = "";
|
||
document.querySelectorAll(".rv-cell").forEach(function (cell) {
|
||
var cc = cell.getAttribute("data-cell");
|
||
getEntries(cell).forEach(function (e) {
|
||
addHidden(fields, "rk_cell", cc);
|
||
addHidden(fields, "rk_sku", e.sku);
|
||
addHidden(fields, "rk_upb", e.upb);
|
||
addHidden(fields, "rk_box", e.box);
|
||
});
|
||
});
|
||
dirty = false;
|
||
var b = document.getElementById("rv-save-btn");
|
||
if (b) b.classList.remove("is-dirty");
|
||
alert(t("저장되었습니다.", "Saved."));
|
||
form.submit();
|
||
}
|
||
|
||
// ── 인쇄 미리보기 ──
|
||
function openPreview() {
|
||
var picked = [].slice.call(document.querySelectorAll(".rv-cell")).filter(function (c) {
|
||
var cb = c.querySelector(".rv-pick");
|
||
return cb && cb.checked;
|
||
});
|
||
if (!picked.length) {
|
||
alert(t("인쇄할 칸을 먼저 선택하세요.", "Select cells to print first."));
|
||
return;
|
||
}
|
||
// 빈 칸은 인쇄 대상에서 제외. 내용 있는 칸만 인쇄.
|
||
var withItems = picked.filter(function (c) { return getEntries(c).length > 0; });
|
||
if (!withItems.length) {
|
||
alert(t("인쇄할 문서가 없습니다. (선택한 칸이 모두 비어 있습니다)",
|
||
"Nothing to print. (All selected cells are empty.)"));
|
||
return;
|
||
}
|
||
var pages = document.getElementById("rvp-pages");
|
||
pages.innerHTML = "";
|
||
withItems.forEach(function (cell) {
|
||
var arr = getEntries(cell);
|
||
var sheet = document.createElement("div");
|
||
sheet.className = "rvp-sheet";
|
||
var list = document.createElement("div");
|
||
list.className = "rvp-list";
|
||
{
|
||
arr.forEach(function (e) {
|
||
var total = (Number(e.upb) || 0) * (Number(e.box) || 0);
|
||
var nm = splitName(nameOf(e));
|
||
var line = document.createElement("div");
|
||
line.className = "rvp-line";
|
||
var main = document.createElement("span");
|
||
main.className = "rvp-name";
|
||
main.textContent = nm.main;
|
||
line.appendChild(main);
|
||
if (nm.size) {
|
||
var size = document.createElement("span");
|
||
size.className = "rvp-size";
|
||
size.textContent = nm.size;
|
||
line.appendChild(size);
|
||
}
|
||
var calc = document.createElement("span");
|
||
calc.className = "rvp-calc";
|
||
calc.innerHTML = fmt(e.upb) + t("개", "ea") + " × "
|
||
+ "<b class=\"rvp-box\">" + fmt(e.box) + t("박스", "box") + "</b>"
|
||
+ " = " + fmt(total);
|
||
line.appendChild(calc);
|
||
list.appendChild(line);
|
||
});
|
||
}
|
||
sheet.appendChild(list);
|
||
pages.appendChild(sheet);
|
||
});
|
||
document.getElementById("rvp-overlay").classList.add("is-open");
|
||
}
|
||
function closePreview() {
|
||
document.getElementById("rvp-overlay").classList.remove("is-open");
|
||
}
|
||
|
||
document.addEventListener("DOMContentLoaded", function () {
|
||
renderAll();
|
||
var board = document.getElementById("rv-board");
|
||
if (board) {
|
||
board.addEventListener("dragstart", onDragStart);
|
||
board.addEventListener("dragover", onDragOver);
|
||
board.addEventListener("dragleave", onDragLeave);
|
||
board.addEventListener("drop", onDrop);
|
||
board.addEventListener("dragend", cleanup);
|
||
board.addEventListener("change", onChange);
|
||
}
|
||
var sb = document.getElementById("rv-save-btn");
|
||
if (sb) sb.addEventListener("click", save);
|
||
var pb = document.getElementById("rv-print-btn");
|
||
if (pb) pb.addEventListener("click", openPreview);
|
||
var pc = document.getElementById("rvp-close");
|
||
if (pc) pc.addEventListener("click", closePreview);
|
||
var pp = document.getElementById("rvp-print");
|
||
if (pp) pp.addEventListener("click", function () { window.print(); });
|
||
var lt = document.getElementById("mys-lang-toggle");
|
||
if (lt) lt.addEventListener("click", function () { setTimeout(renderAll, 0); });
|
||
window.addEventListener("beforeunload", function (ev) {
|
||
if (dirty) { ev.preventDefault(); ev.returnValue = ""; }
|
||
});
|
||
});
|
||
})();
|