fix(cafe24): 옵션 모달 — 옵션 열 축소·품목 표 확대, 창 바닥까지 사용, 품목코드 복사

- 옵션 정의 열 400 → 340px, 모달 폭 1180 → 1280px. 품목 표 열 폭을 줄여
  옵션값 이름이 한 줄(넘치면 말줄임 + title)에 들어가게.
- 모달 높이를 90vh 고정으로 바꾸고 두 열이 바닥까지 늘어나 각자 스크롤
  (품목은 표만 스크롤, 저장 버튼 고정). 첫 구현 때 남은 옛 규칙
  `.cf24-variants-wrap { max-height: 320px }` 가 표 높이를 막고 있었다
  (헤드리스 크롬 실측으로 발견) — 제거.
- 품목코드 클릭 = 클립보드 복사("복사됨" 표시, 비보안 컨텍스트는 execCommand).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
2026-09-18 18:50:28 +09:00
parent e838e8ee15
commit be0d44ded6
3 changed files with 96 additions and 28 deletions
@@ -1,7 +1,7 @@
{% extends "erp_base.html" %}
{% block head_extra %}
<link rel="stylesheet" href="/static/cafe24.css?v=20260918c" />
<link rel="stylesheet" href="/static/cafe24.css?v=20260918d" />
{% endblock %}
{% block content %}
@@ -1007,7 +1007,7 @@
}
// 옵션 정의 편집 (모달 왼쪽 열)
var html = '<div class="cf24-modal-grid"><section class="cf24-modal-col">' +
var html = '<div class="cf24-modal-grid"><section class="cf24-modal-col cf24-modal-col-options">' +
'<h4 class="cf24-side-title">옵션 정의</h4>' +
'<form class="cf24-side-form" id="cf24-opt-form">';
opt.options.forEach(function (o, gi) {
@@ -1041,7 +1041,7 @@
"</section>";
// 품목 표 (모달 오른쪽 열) — 넓은 창이라 한 줄에 다 들어간다.
html += '<section class="cf24-modal-col"><h4 class="cf24-side-title">품목 (' + variants.length + ')</h4>';
html += '<section class="cf24-modal-col cf24-modal-col-variants"><h4 class="cf24-side-title">품목 (' + variants.length + ')</h4>';
if (!variants.length) {
html += '<p class="cf24-muted">품목이 아직 조회되지 않았습니다. 잠시 뒤 창을 닫았다 다시 여세요.</p>';
} else {
@@ -1054,8 +1054,8 @@
var amount = String(parseInt(v.additional_amount || "0", 10) || 0);
html += '<tr class="cf24-v-item" data-code="' + escHtml(v.variant_code) + '">' +
'<td class="cf24-v-imgcol">' + (v.image ? '<img class="cf24-v-img" src="' + escHtml(v.image) + '" alt="" />' : '<span class="cf24-v-img cf24-v-img-empty"></span>') + "</td>" +
'<td class="cf24-v-name">' + escHtml(label) + "</td>" +
'<td class="cf24-v-syscode"><code>' + escHtml(v.variant_code) + "</code></td>" +
'<td class="cf24-v-name" title="' + escHtml(label) + '">' + escHtml(label) + "</td>" +
'<td class="cf24-v-syscode"><button type="button" class="cf24-v-copy" data-copy-text="' + escHtml(v.variant_code) + '" title="클릭하면 품목코드를 복사합니다">' + escHtml(v.variant_code) + "</button></td>" +
'<td class="cf24-v-code"><input type="text" name="custom_variant_code" maxlength="40" value="' + escHtml(v.custom_variant_code) + '" /></td>' +
'<td class="cf24-v-amount"><input type="text" name="additional_amount" inputmode="numeric" value="' + escHtml(amount) + '" /></td>' +
'<td class="cf24-v-flag"><button type="button" class="cf24-v-toggle' + (v.display ? " is-on" : "") + '" data-flag="display" data-on="' + (v.display ? 1 : 0) + '" data-initial="' + (v.display ? 1 : 0) + '">' + (v.display ? "진열" : "미진열") + "</button></td>" +
@@ -1147,6 +1147,34 @@
if (!saveBtn) return;
var wrap = body.querySelector(".cf24-variants-wrap");
trackDirty(wrap);
// 품목코드 클릭 = 클립보드 복사 (HTTPS 가 아니면 execCommand 로 대체)
wrap.querySelectorAll(".cf24-v-copy").forEach(function (btn) {
btn.addEventListener("click", function () {
var text = btn.dataset.copyText || btn.textContent;
var done = function () {
var old = btn.textContent;
btn.textContent = "복사됨";
btn.classList.add("is-copied");
setTimeout(function () { btn.textContent = old; btn.classList.remove("is-copied"); }, 1200);
};
if (navigator.clipboard && window.isSecureContext) {
navigator.clipboard.writeText(text).then(done, function () { fallbackCopy(text, done); });
} else {
fallbackCopy(text, done);
}
});
});
function fallbackCopy(text, done) {
var ta = document.createElement("textarea");
ta.value = text;
ta.setAttribute("readonly", "");
ta.style.position = "fixed";
ta.style.opacity = "0";
document.body.appendChild(ta);
ta.select();
try { if (document.execCommand("copy")) done(); } catch (e) { /* 복사 불가 — 조용히 무시 */ }
document.body.removeChild(ta);
}
wrap.querySelectorAll(".cf24-v-toggle").forEach(function (btn) {
btn.addEventListener("click", function () {
var on = btn.dataset.on !== "1";
+62 -22
View File
@@ -1130,8 +1130,10 @@ body.cf24-modal-open {
.cf24-modal-box {
display: flex;
flex-direction: column;
width: min(1180px, 96vw);
max-height: 90vh;
width: min(1280px, 96vw);
/* max-height 가 아니라 height 고정 — 두 열이 창 바닥까지 늘어나고 각 열 안에서
스크롤한다(품목이 많을 때 표만 스크롤, 저장 버튼은 항상 보임). */
height: 90vh;
background: var(--color-canvas-white, #fff);
border-radius: var(--r-xl, 14px);
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
@@ -1176,27 +1178,52 @@ body.cf24-modal-open {
}
.cf24-modal-body {
padding: 16px 18px 20px;
overflow: auto;
padding: 16px 18px 18px;
flex: 1 1 auto;
min-height: 0;
display: flex;
flex-direction: column;
}
/* 옵션 정의(왼쪽, 좁게) | 품목 표(오른쪽, 남는 폭 전부). 두 열 모두 창 바닥까지. */
.cf24-modal-grid {
flex: 1 1 auto;
min-height: 0;
display: grid;
grid-template-columns: 400px minmax(0, 1fr);
grid-template-columns: 340px minmax(0, 1fr);
grid-template-rows: minmax(0, 1fr);
gap: 24px;
align-items: start;
align-items: stretch;
}
@media (max-width: 960px) {
.cf24-modal-grid {
grid-template-columns: minmax(0, 1fr);
grid-template-rows: auto;
}
}
.cf24-modal-col {
min-width: 0;
min-height: 0;
display: flex;
flex-direction: column;
}
/* 왼쪽 열은 통째로 스크롤, 오른쪽 열은 표만 스크롤(제목·저장 버튼 고정) */
.cf24-modal-col-options {
overflow: auto;
}
.cf24-modal-col-variants > .cf24-variants-wrap {
flex: 1 1 auto;
min-height: 0;
overflow: auto;
border-bottom: 1px solid var(--color-subtle-ash, #e5e5e5);
}
.cf24-modal-col-variants > .cf24-side-actions {
flex: 0 0 auto;
}
/* 옵션이 없는 상품의 생성 폼 — 넓은 모달에서 끝까지 늘어나지 않게 */
@@ -1229,18 +1256,37 @@ body.cf24-modal-open {
background: var(--color-canvas-white, #fff);
}
.cf24-variants-wrap {
max-height: 62vh;
overflow: auto;
/* 열 폭 — 옵션 이름이 한 줄에 들어가도록 나머지를 좁게 잡는다
(1280px 모달: 340 옵션 + 24 간격 → 표 약 880px, 이름 칸 약 420px). */
.cf24-v-imgcol { width: 40px; }
.cf24-v-name { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 0; }
.cf24-v-syscode { width: 104px; }
.cf24-v-code { width: 132px; }
.cf24-v-amount { width: 90px; }
.cf24-v-amount input { text-align: right; }
.cf24-v-flag { width: 56px; text-align: center; }
/* 품목코드 — 클릭하면 클립보드로. 눌리는 것임을 커서·밑줄로 알린다. */
.cf24-v-copy {
font-family: var(--font-geist-mono, ui-monospace, monospace);
font-size: 11px;
color: var(--color-midtone-gray, #737373);
background: transparent;
border: 0;
padding: 2px 4px;
border-radius: var(--r-sm, 4px);
cursor: copy;
white-space: nowrap;
}
.cf24-v-imgcol { width: 40px; }
.cf24-v-syscode { width: 110px; }
.cf24-v-syscode code { font-size: 11px; color: var(--color-midtone-gray, #737373); }
.cf24-v-code { width: 150px; }
.cf24-v-amount { width: 96px; }
.cf24-v-amount input { text-align: right; }
.cf24-v-flag { width: 58px; text-align: center; }
.cf24-v-copy:hover {
background: var(--color-ghost-gray, #f2f2f2);
color: var(--color-rich-black, #0a0a0a);
}
.cf24-v-copy.is-copied {
color: var(--color-success-green, #10c22b);
}
.cf24-v-img {
width: 32px;
@@ -1258,7 +1304,6 @@ body.cf24-modal-open {
.cf24-v-name {
font-weight: 500;
word-break: break-word;
}
.cf24-variants input[type="text"] {
@@ -1292,11 +1337,6 @@ body.cf24-modal-open {
box-shadow: 0 0 0 2px #f0b429;
}
.cf24-variants-wrap {
max-height: 320px;
overflow: auto;
}
.cf24-opt-danger {
display: flex;
justify-content: flex-end;