fix(cupang): 도장은 또렷하게, 애니메이션은 방금 완료된 것만

- 혼합 박스 도장이 흐리게 보이던 원인: 부모(.cpg-box-title)에 opacity 를
  주면 자식 도장까지 함께 흐려진다 → 제목은 색만 낮추고 도장은 opacity 1.
- 렌더할 때마다 모든 도장에 애니메이션이 다시 걸리던 문제: 이미 찍힌
  항목을 stamped 로 기억해 새로 완료된 도장(.is-new)에만 애니메이션.
  잔여가 다시 생기거나 재계산하면 기록을 지워 다음 완료 때 다시 찍힌다.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-29 03:55:35 +09:00
parent 2779a6285d
commit 2d84a20814
9 changed files with 34 additions and 16 deletions
@@ -1,6 +1,6 @@
{% extends "erp_base.html" %} {% extends "erp_base.html" %}
{% block head_extra %}<link rel="stylesheet" href="/static/cupang.css?v=20260829j" />{% endblock %} {% block head_extra %}<link rel="stylesheet" href="/static/cupang.css?v=20260829k" />{% endblock %}
{% block content %} {% block content %}
<section class="cpg"> <section class="cpg">
@@ -248,7 +248,8 @@
}); });
}); });
var had = Object.keys(alloc).some(function (k) { return alloc[k].length; }); var had = Object.keys(alloc).some(function (k) { return alloc[k].length; });
alloc = {}; // 계산이 바뀌면 이전 배분은 근거가 사라지므로 비운다. alloc = {};
stamped = {}; // 계산이 바뀌면 이전 배분은 근거가 사라지므로 비운다.
render(); render();
msg.textContent = "계산 완료 (" + calc.results.length + "건)" + (had ? " — 센터 분배 초기화됨" : ""); msg.textContent = "계산 완료 (" + calc.results.length + "건)" + (had ? " — 센터 분배 초기화됨" : "");
}) })
@@ -303,9 +304,12 @@
"</svg>"; "</svg>";
} }
// 배분이 끝난 카드에 찍는 도장. // 배분이 끝난 카드에 찍는 도장. 애니메이션은 "이번에 막 완료된" 것만.
function stampHtml() { var stamped = {}; // key -> 이미 도장이 찍혀 있던 항목
return '<span class="cpg-stamp" aria-label="배분 완료">배분 완료</span>'; function stampHtml(key) {
var fresh = !stamped[key];
stamped[key] = true;
return '<span class="cpg-stamp' + (fresh ? " is-new" : "") + '" aria-label="배분 완료">배분 완료</span>';
} }
function kpi(label, value, hint) { function kpi(label, value, hint) {
@@ -348,11 +352,12 @@
var key = prodKey(r.product_code); var key = prodKey(r.product_code);
var remain = remainFor(key); var remain = remainFor(key);
var used = r.full_boxes - remain; var used = r.full_boxes - remain;
if (remain) delete stamped[key];
ph += '<div class="cpg-sum-prod' + (remain ? " is-draggable" : " is-done") + '"' + ph += '<div class="cpg-sum-prod' + (remain ? " is-draggable" : " is-done") + '"' +
' data-key="' + esc(key) + '">' + ' data-key="' + esc(key) + '">' +
'<div class="cpg-sum-prod-head">' + '<div class="cpg-sum-prod-head">' +
'<span class="cpg-sum-prod-name">' + esc(r.product_name) + "</span>" + '<span class="cpg-sum-prod-name">' + esc(r.product_name) + "</span>" +
(remain ? "" : stampHtml()) + (remain ? "" : stampHtml(key)) +
"</div>" + "</div>" +
'<div class="cpg-sum-prod-nums">' + '<div class="cpg-sum-prod-nums">' +
"<strong>" + remain + "박스</strong>" + "<strong>" + remain + "박스</strong>" +
@@ -376,6 +381,7 @@
m.boxes.forEach(function (b, i) { m.boxes.forEach(function (b, i) {
var key = mixKey(m.box_name, i); var key = mixKey(m.box_name, i);
var remain = remainFor(key); var remain = remainFor(key);
if (remain) delete stamped[key];
var items = b.items.map(function (it) { var items = b.items.map(function (it) {
return "<li><span>" + esc(it.product_name) + "</span><b>" + it.quantity + "개</b></li>"; return "<li><span>" + esc(it.product_name) + "</span><b>" + it.quantity + "개</b></li>";
}).join(""); }).join("");
@@ -385,7 +391,7 @@
'<div class="cpg-box-art">' + boxSvg(b.fill_percent, m.box_name) + "</div>" + '<div class="cpg-box-art">' + boxSvg(b.fill_percent, m.box_name) + "</div>" +
'<div class="cpg-box-info">' + '<div class="cpg-box-info">' +
'<div class="cpg-box-title">' + esc(m.box_name) + " 혼합 #" + (i + 1) + '<div class="cpg-box-title">' + esc(m.box_name) + " 혼합 #" + (i + 1) +
(remain ? "" : stampHtml()) + "</div>" + (remain ? "" : stampHtml(key)) + "</div>" +
'<ul class="cpg-box-items">' + items + "</ul>" + '<ul class="cpg-box-items">' + items + "</ul>" +
'<div class="cpg-box-bar"><span style="width:' + b.fill_percent + '%"></span></div>' + '<div class="cpg-box-bar"><span style="width:' + b.fill_percent + '%"></span></div>' +
'<div class="cpg-box-fillnum">' + b.fill_percent + "% 채움 · " + units[key] + "개</div>" + '<div class="cpg-box-fillnum">' + b.fill_percent + "% 채움 · " + units[key] + "개</div>" +
@@ -1,6 +1,6 @@
{% extends "erp_base.html" %} {% extends "erp_base.html" %}
{% block head_extra %}<link rel="stylesheet" href="/static/cupang.css?v=20260829j" />{% endblock %} {% block head_extra %}<link rel="stylesheet" href="/static/cupang.css?v=20260829k" />{% endblock %}
{% block content %} {% block content %}
<section class="cpg"> <section class="cpg">
@@ -1,6 +1,6 @@
{% extends "erp_base.html" %} {% extends "erp_base.html" %}
{% block head_extra %}<link rel="stylesheet" href="/static/cupang.css?v=20260829j" />{% endblock %} {% block head_extra %}<link rel="stylesheet" href="/static/cupang.css?v=20260829k" />{% endblock %}
{% block content %} {% block content %}
<section class="cpg"> <section class="cpg">
@@ -1,6 +1,6 @@
{% extends "erp_base.html" %} {% extends "erp_base.html" %}
{% block head_extra %}<link rel="stylesheet" href="/static/cupang.css?v=20260829j" />{% endblock %} {% block head_extra %}<link rel="stylesheet" href="/static/cupang.css?v=20260829k" />{% endblock %}
{% block content %} {% block content %}
<section class="cpg"> <section class="cpg">
@@ -1,6 +1,6 @@
{% extends "erp_base.html" %} {% extends "erp_base.html" %}
{% block head_extra %}<link rel="stylesheet" href="/static/cupang.css?v=20260829j" />{% endblock %} {% block head_extra %}<link rel="stylesheet" href="/static/cupang.css?v=20260829k" />{% endblock %}
{% block content %} {% block content %}
<section class="cpg"> <section class="cpg">
@@ -1,6 +1,6 @@
{% extends "erp_base.html" %} {% extends "erp_base.html" %}
{% block head_extra %}<link rel="stylesheet" href="/static/cupang.css?v=20260829j" />{% endblock %} {% block head_extra %}<link rel="stylesheet" href="/static/cupang.css?v=20260829k" />{% endblock %}
{% block content %} {% block content %}
<section class="cpg"> <section class="cpg">
@@ -1,6 +1,6 @@
{% extends "erp_base.html" %} {% extends "erp_base.html" %}
{% block head_extra %}<link rel="stylesheet" href="/static/cupang.css?v=20260829j" />{% endblock %} {% block head_extra %}<link rel="stylesheet" href="/static/cupang.css?v=20260829k" />{% endblock %}
{% block content %} {% block content %}
<section class="cpg"> <section class="cpg">
+4 -3
View File
@@ -748,8 +748,8 @@ body.cpg-dragging { cursor: grabbing; user-select: none; }
opacity: .85; opacity: .85;
transform: rotate(-7deg); transform: rotate(-7deg);
box-shadow: inset 0 0 0 1px rgba(29, 95, 208, .25); box-shadow: inset 0 0 0 1px rgba(29, 95, 208, .25);
animation: cpg-stamp-in .22s ease-out;
} }
.cpg-stamp.is-new { animation: cpg-stamp-in .22s ease-out; }
@keyframes cpg-stamp-in { @keyframes cpg-stamp-in {
from { opacity: 0; transform: rotate(-7deg) scale(1.6); } from { opacity: 0; transform: rotate(-7deg) scale(1.6); }
to { opacity: .85; transform: rotate(-7deg) scale(1); } to { opacity: .85; transform: rotate(-7deg) scale(1); }
@@ -758,12 +758,13 @@ body.cpg-dragging { cursor: grabbing; user-select: none; }
/* 배분이 끝난 카드는 흐리게 — 글자·상자 아이콘 모두 */ /* 배분이 끝난 카드는 흐리게 — 글자·상자 아이콘 모두 */
.cpg-sum-prod.is-done, .cpg-box-card.is-done { opacity: 1; } .cpg-sum-prod.is-done, .cpg-box-card.is-done { opacity: 1; }
.cpg-sum-prod.is-done .cpg-sum-prod-name, .cpg-sum-prod.is-done .cpg-sum-prod-name,
.cpg-box-card.is-done .cpg-box-title { color: var(--color-midtone-gray); }
.cpg-sum-prod.is-done .cpg-sum-prod-nums, .cpg-sum-prod.is-done .cpg-sum-prod-nums,
.cpg-box-card.is-done .cpg-box-title,
.cpg-box-card.is-done .cpg-box-items, .cpg-box-card.is-done .cpg-box-items,
.cpg-box-card.is-done .cpg-box-fillnum { color: var(--color-midtone-gray); opacity: .6; } .cpg-box-card.is-done .cpg-box-fillnum { color: var(--color-midtone-gray); opacity: .6; }
.cpg-sum-prod.is-done .cpg-sum-prod-nums strong, .cpg-sum-prod.is-done .cpg-sum-prod-nums strong,
.cpg-box-card.is-done .cpg-box-items b { color: var(--color-midtone-gray); } .cpg-box-card.is-done .cpg-box-items b { color: var(--color-midtone-gray); }
.cpg-box-card.is-done .cpg-box-svg { filter: grayscale(1); opacity: .45; } .cpg-box-card.is-done .cpg-box-svg { filter: grayscale(1); opacity: .45; }
.cpg-box-card.is-done .cpg-box-bar { opacity: .4; } .cpg-box-card.is-done .cpg-box-bar { opacity: .4; }
.cpg-sum-prod.is-done .cpg-stamp, .cpg-box-card.is-done .cpg-stamp { opacity: .85; } /* 도장은 카드가 흐려져도 또렷하게 유지 */
.cpg-sum-prod.is-done .cpg-stamp, .cpg-box-card.is-done .cpg-stamp { opacity: 1; }
+11
View File
@@ -123,10 +123,21 @@ function dragCardTo(card, target) {
text(".cpg-sum-prod .cpg-sum-prod-nums")); text(".cpg-sum-prod .cpg-sum-prod-nums"));
check("잔여 0 카드는 드래그 불가", $$(".cpg-sum-prod")[0].classList.contains("is-done")); check("잔여 0 카드는 드래그 불가", $$(".cpg-sum-prod")[0].classList.contains("is-done"));
check("배분 완료 도장 표시", !!$$(".cpg-sum-prod")[0].querySelector(".cpg-stamp")); check("배분 완료 도장 표시", !!$$(".cpg-sum-prod")[0].querySelector(".cpg-stamp"));
check("막 완료된 도장은 애니메이션", $$(".cpg-sum-prod")[0].querySelector(".cpg-stamp").classList.contains("is-new"));
check("제품 카드에 박스명 배지 없음", !$$(".cpg-sum-prod")[0].querySelector(".erp-badge")); check("제품 카드에 박스명 배지 없음", !$$(".cpg-sum-prod")[0].querySelector(".erp-badge"));
check("센터 합계 2박스/16개", text(".cpg-dist-sum").replace(/\s/g, "").startsWith("2박스16개"), check("센터 합계 2박스/16개", text(".cpg-dist-sum").replace(/\s/g, "").startsWith("2박스16개"),
text(".cpg-dist-sum")); text(".cpg-dist-sum"));
// 다른 카드를 담아 다시 렌더해도, 이미 찍힌 도장은 애니메이션을 반복하지 않는다
$$(".cpg-sum-prod")[1].click();
$("#cpg-dlg-all").click();
check("기존 도장은 애니메이션 반복 없음",
!$$(".cpg-sum-prod")[0].querySelector(".cpg-stamp").classList.contains("is-new"));
check("새로 완료된 도장만 애니메이션",
$$(".cpg-sum-prod")[1].querySelector(".cpg-stamp").classList.contains("is-new"));
// 되돌려서 뒷 시나리오에 영향 없게
$$(".cpg-dist-del")[1].click();
// ── 수량 직접 수정 (상한 = 잔여 + 자기 수량) ────── // ── 수량 직접 수정 (상한 = 잔여 + 자기 수량) ──────
const qty = $(".cpg-dist-item .cpg-dist-qty"); const qty = $(".cpg-dist-item .cpg-dist-qty");
qty.value = "5"; fire(qty, "input"); qty.value = "5"; fire(qty, "input");