fix(cafe24): 옵션값 썸네일 탐색 확대 + 품목을 2줄 카드로
- 옵션값 썸네일이 option_image_file 만 봐서 "없음"으로 나왔다. 카페24 관리자에서 "옵션 연결 이미지"로만 등록한 상품은 그 필드가 비어 있고 품목 image 에만 나타난다(실물). option_image_file → option_link_image → 해당 옵션값을 가진 품목의 image 순으로 찾는다. - 품목 표가 380px 패널에 좁아 옵션값 이름이 아이콘 아래로 꺾여 잘렸다. 품목마다 2줄 카드(아이콘+이름 / 자체코드·추가금액·진열·판매)로 교체. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
{% extends "erp_base.html" %}
|
{% extends "erp_base.html" %}
|
||||||
|
|
||||||
{% block head_extra %}
|
{% block head_extra %}
|
||||||
<link rel="stylesheet" href="/static/cafe24.css?v=20260918a" />
|
<link rel="stylesheet" href="/static/cafe24.css?v=20260918b" />
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
@@ -938,6 +938,21 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 옵션값 썸네일은 세 곳 중 있는 것을 보여준다 — 옵션 버튼 이미지(option_image_file)
|
||||||
|
// → 옵션 연결 이미지(option_link_image) → 그 옵션값을 가진 품목의 image.
|
||||||
|
// 카페24 관리자에서 "옵션 연결 이미지"로만 등록한 상품은 option_image_file 이
|
||||||
|
// 비어 있고 품목 image 에만 나타난다(실물 — 버튼 이미지만 보면 "없음"으로 오인).
|
||||||
|
function variantImageFor(name, value) {
|
||||||
|
for (var i = 0; i < variants.length; i++) {
|
||||||
|
var v = variants[i];
|
||||||
|
if (!v.image) continue;
|
||||||
|
for (var j = 0; j < v.options.length; j++) {
|
||||||
|
if (v.options[j].name === name && v.options[j].value === value) return v.image;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
// 옵션 정의 편집
|
// 옵션 정의 편집
|
||||||
var html = '<form class="cf24-side-form" id="cf24-opt-form">';
|
var html = '<form class="cf24-side-form" id="cf24-opt-form">';
|
||||||
opt.options.forEach(function (o, gi) {
|
opt.options.forEach(function (o, gi) {
|
||||||
@@ -947,8 +962,10 @@
|
|||||||
' <label class="cf24-side-field cf24-side-field-type"><span>표시방식</span>' + typeSelect(o.option_display_type || "S", "option_display_type") + "</label>" +
|
' <label class="cf24-side-field cf24-side-field-type"><span>표시방식</span>' + typeSelect(o.option_display_type || "S", "option_display_type") + "</label>" +
|
||||||
"</div>";
|
"</div>";
|
||||||
o.option_value.forEach(function (v, vi) {
|
o.option_value.forEach(function (v, vi) {
|
||||||
var thumb = v.option_image_file
|
var thumbSrc = v.option_image_file || v.option_link_image || variantImageFor(o.option_name, v.option_text);
|
||||||
? '<img class="cf24-opt-thumb" src="' + escHtml(v.option_image_file) + '" alt="" />'
|
var thumb = thumbSrc
|
||||||
|
? '<img class="cf24-opt-thumb" src="' + escHtml(thumbSrc) + '" alt="" title="' +
|
||||||
|
(v.option_image_file ? "옵션 버튼 이미지" : v.option_link_image ? "옵션 연결 이미지" : "품목 이미지") + '" />'
|
||||||
: '<div class="cf24-opt-thumb-empty">없음</div>';
|
: '<div class="cf24-opt-thumb-empty">없음</div>';
|
||||||
html += '<div class="cf24-opt-value" data-value="' + vi + '">' +
|
html += '<div class="cf24-opt-value" data-value="' + vi + '">' +
|
||||||
'<span class="cf24-opt-thumb-slot">' + thumb + "</span>" +
|
'<span class="cf24-opt-thumb-slot">' + thumb + "</span>" +
|
||||||
@@ -966,28 +983,29 @@
|
|||||||
' <button class="erp-btn erp-btn-primary" type="submit">옵션 저장</button></div>' +
|
' <button class="erp-btn erp-btn-primary" type="submit">옵션 저장</button></div>' +
|
||||||
"</form>";
|
"</form>";
|
||||||
|
|
||||||
// 품목 표
|
// 품목 — 패널 폭(380px)에 표는 좁다(실측: 이름이 이미지 아래로 꺾여 잘림).
|
||||||
|
// 품목마다 2줄 카드: 1줄 아이콘 + 옵션값 이름, 2줄 자체코드·추가금액·진열·판매.
|
||||||
html += '<div class="cf24-opt-group"><h5 class="cf24-side-title">품목 (' + variants.length + ')</h5>';
|
html += '<div class="cf24-opt-group"><h5 class="cf24-side-title">품목 (' + variants.length + ')</h5>';
|
||||||
if (!variants.length) {
|
if (!variants.length) {
|
||||||
html += '<p class="cf24-muted">품목이 아직 조회되지 않았습니다. 잠시 뒤 「옵션 / 품목」을 접었다 펴서 다시 불러오세요.</p>';
|
html += '<p class="cf24-muted">품목이 아직 조회되지 않았습니다. 잠시 뒤 「옵션 / 품목」을 접었다 펴서 다시 불러오세요.</p>';
|
||||||
} else {
|
} else {
|
||||||
html += '<div class="cf24-variants-wrap"><table class="cf24-variants"><thead><tr>' +
|
html += '<div class="cf24-variants-wrap">';
|
||||||
"<th>옵션</th><th class=\"cf24-v-code\">자체코드</th><th class=\"cf24-v-amount\">추가금액</th>" +
|
|
||||||
'<th class="cf24-v-flag">진열</th><th class="cf24-v-flag">판매</th></tr></thead><tbody>';
|
|
||||||
variants.forEach(function (v) {
|
variants.forEach(function (v) {
|
||||||
var label = v.options.map(function (o) { return o.value; }).join(" / ") || v.variant_code;
|
var label = v.options.map(function (o) { return o.value; }).join(" / ") || v.variant_code;
|
||||||
var amount = String(parseInt(v.additional_amount || "0", 10) || 0);
|
var amount = String(parseInt(v.additional_amount || "0", 10) || 0);
|
||||||
html += '<tr data-code="' + escHtml(v.variant_code) + '">' +
|
html += '<div class="cf24-v-item" data-code="' + escHtml(v.variant_code) + '">' +
|
||||||
'<td class="cf24-v-name" title="' + escHtml(v.variant_code) + '">' +
|
'<div class="cf24-v-head" title="' + escHtml(v.variant_code) + '">' +
|
||||||
(v.image ? '<img class="cf24-opt-thumb" style="width:28px;height:28px;vertical-align:middle;margin-right:4px" src="' + escHtml(v.image) + '" alt="" />' : "") +
|
(v.image ? '<img class="cf24-v-img" src="' + escHtml(v.image) + '" alt="" />' : '<span class="cf24-v-img cf24-v-img-empty"></span>') +
|
||||||
escHtml(label) + "</td>" +
|
'<span class="cf24-v-name">' + escHtml(label) + "</span></div>" +
|
||||||
'<td class="cf24-v-code"><input type="text" name="custom_variant_code" maxlength="40" value="' + escHtml(v.custom_variant_code) + '" /></td>' +
|
'<div class="cf24-v-row">' +
|
||||||
'<td class="cf24-v-amount"><input type="text" name="additional_amount" inputmode="numeric" value="' + escHtml(amount) + '" /></td>' +
|
'<label class="cf24-v-code"><span>자체코드</span><input type="text" name="custom_variant_code" maxlength="40" value="' + escHtml(v.custom_variant_code) + '" /></label>' +
|
||||||
'<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>" +
|
'<label class="cf24-v-amount"><span>추가금액</span><input type="text" name="additional_amount" inputmode="numeric" value="' + escHtml(amount) + '" /></label>' +
|
||||||
'<td class="cf24-v-flag"><button type="button" class="cf24-v-toggle' + (v.selling ? " is-on" : "") + '" data-flag="selling" data-on="' + (v.selling ? 1 : 0) + '" data-initial="' + (v.selling ? 1 : 0) + '">' + (v.selling ? "판매" : "중지") + "</button></td>" +
|
'<span class="cf24-v-flags">' +
|
||||||
"</tr>";
|
'<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>" +
|
||||||
|
'<button type="button" class="cf24-v-toggle' + (v.selling ? " is-on" : "") + '" data-flag="selling" data-on="' + (v.selling ? 1 : 0) + '" data-initial="' + (v.selling ? 1 : 0) + '">' + (v.selling ? "판매" : "중지") + "</button>" +
|
||||||
|
"</span></div></div>";
|
||||||
});
|
});
|
||||||
html += "</tbody></table></div>" +
|
html += "</div>" +
|
||||||
'<div class="cf24-side-actions" style="margin-top:8px;"><span class="cf24-muted" id="cf24-var-msg"></span>' +
|
'<div class="cf24-side-actions" style="margin-top:8px;"><span class="cf24-muted" id="cf24-var-msg"></span>' +
|
||||||
' <button type="button" class="erp-btn erp-btn-primary" id="cf24-var-save">품목 저장</button></div>';
|
' <button type="button" class="erp-btn erp-btn-primary" id="cf24-var-save">품목 저장</button></div>';
|
||||||
}
|
}
|
||||||
@@ -1085,7 +1103,7 @@
|
|||||||
var msg = body.querySelector("#cf24-var-msg");
|
var msg = body.querySelector("#cf24-var-msg");
|
||||||
var rows = [];
|
var rows = [];
|
||||||
var lines = [];
|
var lines = [];
|
||||||
wrap.querySelectorAll("tr[data-code]").forEach(function (tr) {
|
wrap.querySelectorAll(".cf24-v-item[data-code]").forEach(function (tr) {
|
||||||
var row = { variant_code: tr.dataset.code };
|
var row = { variant_code: tr.dataset.code };
|
||||||
var any = false;
|
var any = false;
|
||||||
var code = tr.querySelector('[name="custom_variant_code"]');
|
var code = tr.querySelector('[name="custom_variant_code"]');
|
||||||
@@ -1111,7 +1129,7 @@
|
|||||||
var updated = data.updated || {};
|
var updated = data.updated || {};
|
||||||
Object.keys(updated).forEach(function (code) {
|
Object.keys(updated).forEach(function (code) {
|
||||||
var u = updated[code];
|
var u = updated[code];
|
||||||
var tr = wrap.querySelector('tr[data-code="' + code + '"]');
|
var tr = wrap.querySelector('.cf24-v-item[data-code="' + code + '"]');
|
||||||
if (!tr) return;
|
if (!tr) return;
|
||||||
var codeEl = tr.querySelector('[name="custom_variant_code"]');
|
var codeEl = tr.querySelector('[name="custom_variant_code"]');
|
||||||
var amountEl = tr.querySelector('[name="additional_amount"]');
|
var amountEl = tr.querySelector('[name="additional_amount"]');
|
||||||
|
|||||||
+58
-25
@@ -1106,33 +1106,61 @@
|
|||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 품목 표 — 좁은 칸에 맞춘 작은 글자 */
|
/* 품목 — 380px 패널에 표는 좁다(실측: 옵션값 이름이 아이콘 아래로 꺾여 "그/1개"로
|
||||||
.cf24-variants {
|
잘렸다). 품목마다 2줄 카드로 그린다. 1줄: 아이콘 + 이름(남는 폭 전부),
|
||||||
width: 100%;
|
2줄: 자체코드 · 추가금액 · 진열/판매 토글. */
|
||||||
border-collapse: collapse;
|
.cf24-v-item {
|
||||||
font-size: 12px;
|
padding: 6px 0;
|
||||||
table-layout: fixed;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cf24-variants th,
|
|
||||||
.cf24-variants td {
|
|
||||||
padding: 4px 4px;
|
|
||||||
border-bottom: 1px solid var(--color-subtle-ash, #e5e5e5);
|
border-bottom: 1px solid var(--color-subtle-ash, #e5e5e5);
|
||||||
vertical-align: middle;
|
|
||||||
text-align: left;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.cf24-variants th {
|
.cf24-v-head {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cf24-v-img {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
object-fit: cover;
|
||||||
|
border: 1px solid var(--color-subtle-ash, #e5e5e5);
|
||||||
|
border-radius: var(--r-sm, 4px);
|
||||||
|
background: var(--color-ghost-gray, #f6f8fa);
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cf24-v-img-empty {
|
||||||
|
border-style: dashed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cf24-v-name {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
min-width: 0;
|
||||||
|
font-size: 13px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
color: var(--color-midtone-gray, #737373);
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cf24-variants td.cf24-v-name {
|
|
||||||
word-break: break-word;
|
word-break: break-word;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cf24-variants input[type="text"] {
|
.cf24-v-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-end;
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cf24-v-row label {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 2px;
|
||||||
|
font-size: 11px;
|
||||||
|
color: var(--color-midtone-gray, #737373);
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cf24-v-row input[type="text"] {
|
||||||
height: 28px;
|
height: 28px;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@@ -1142,11 +1170,16 @@
|
|||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cf24-variants .cf24-v-amount input { text-align: right; }
|
.cf24-v-code { flex: 1 1 110px; }
|
||||||
/* "미진열"(3글자)이 한 줄에 들어가는 폭. 실측: 44px 에서는 두 줄로 꺾였다. */
|
.cf24-v-amount { flex: 0 0 84px; }
|
||||||
.cf24-variants .cf24-v-flag { text-align: center; width: 54px; }
|
.cf24-v-amount input { text-align: right; }
|
||||||
.cf24-variants .cf24-v-code { width: 92px; }
|
|
||||||
.cf24-variants .cf24-v-amount { width: 76px; }
|
.cf24-v-flags {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
display: flex;
|
||||||
|
gap: 4px;
|
||||||
|
padding-bottom: 3px; /* 토글을 입력칸 세로 중심에 맞춘다 */
|
||||||
|
}
|
||||||
|
|
||||||
/* 진열/판매 토글 — 저장 전까지는 화면만 바뀐다(배지와 같은 색 규칙) */
|
/* 진열/판매 토글 — 저장 전까지는 화면만 바뀐다(배지와 같은 색 규칙) */
|
||||||
.cf24-v-toggle {
|
.cf24-v-toggle {
|
||||||
|
|||||||
@@ -456,7 +456,12 @@ cafe24_oauth_tokens 저장
|
|||||||
"requests":[{variant_code, custom_variant_code, additional_amount, display, selling…}]}`
|
"requests":[{variant_code, custom_variant_code, additional_amount, display, selling…}]}`
|
||||||
1회 100건(초과 시 나눠 보냄). 재고(`quantity`)는 이 화면에서 건드리지 않는다.
|
1회 100건(초과 시 나눠 보냄). 재고(`quantity`)는 이 화면에서 건드리지 않는다.
|
||||||
- "옵션별 상품 이름" 은 옵션값(`option_text`)이고, "옵션별 썸네일" 은 `option_image_file`
|
- "옵션별 상품 이름" 은 옵션값(`option_text`)이고, "옵션별 썸네일" 은 `option_image_file`
|
||||||
(+ `option_link_image` 에도 같은 경로) 이며, "옵션별 상품코드/추가금액/진열" 은 품목
|
(+ `option_link_image` 에도 같은 경로) 이며,
|
||||||
|
화면의 옵션값 썸네일은 **`option_image_file` → `option_link_image` → 그 옵션값을 가진
|
||||||
|
품목의 `image`** 순으로 있는 것을 보여준다. 카페24 관리자에서 "옵션 연결 이미지"로만
|
||||||
|
등록한 상품은 `option_image_file` 이 비어 있고 품목 `image` 에만 나타난다(실물 —
|
||||||
|
버튼 이미지만 보면 "없음"으로 오인했던 사고). 품목은 380px 패널에 표가 좁아
|
||||||
|
품목마다 2줄 카드(아이콘+이름 / 코드·금액·진열·판매)로 그린다. "옵션별 상품코드/추가금액/진열" 은 품목
|
||||||
(`custom_variant_code`/`additional_amount`/`display`)이다 — 카페24 모델상 둘은 다른
|
(`custom_variant_code`/`additional_amount`/`display`)이다 — 카페24 모델상 둘은 다른
|
||||||
리소스라 화면에서도 「옵션 저장」과 「품목 저장」이 따로 있다.
|
리소스라 화면에서도 「옵션 저장」과 「품목 저장」이 따로 있다.
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user