feat(dispatch): 한글/영문 토글(말레이시아 직원용)
- _nav 에 KO/EN 토글 버튼 + 공용 JS(data-ko/data-en 텍스트, data-*-ph placeholder 교체, localStorage 저장 → 전 페이지 공통, dsp:lang 이벤트 발행) - batches/new/detail/picking/handover 정적 라벨 이중언어화 - 동적 텍스트도 언어 반영: 완료 N/M 라벨, 일괄 토글 확인창, 업로드 안내/파일 라벨 - 카드 작업 버튼은 기존대로 한/영 동시 표기 유지 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -1,12 +1,61 @@
|
||||
{# 말레이시아 배송 공용 상단 바: 목록/업로드 + (배치 진입 시) 작업/피킹/전달 서브탭 #}
|
||||
{# 말레이시아 배송 공용 상단 바: 목록/업로드 + (배치 진입 시) 작업/피킹/전달 서브탭
|
||||
+ 한글/영문 토글(localStorage 저장, 전 페이지 공통). #}
|
||||
<div class="erp-page-actions" style="display:flex;gap:8px;flex-wrap:wrap;align-items:center;">
|
||||
<a class="erp-btn {% if active_tab=='batches' %}erp-btn-primary{% else %}erp-btn-outline{% endif %}" href="/dispatch/">출고 배치</a>
|
||||
<a class="erp-btn {% if active_tab=='new' %}erp-btn-primary{% else %}erp-btn-outline{% endif %}" href="/dispatch/batches/new">+ 새 업로드</a>
|
||||
<a class="erp-btn {% if active_tab=='batches' %}erp-btn-primary{% else %}erp-btn-outline{% endif %}" href="/dispatch/"
|
||||
data-ko="출고 배치" data-en="Batches">출고 배치</a>
|
||||
<a class="erp-btn {% if active_tab=='new' %}erp-btn-primary{% else %}erp-btn-outline{% endif %}" href="/dispatch/batches/new"
|
||||
data-ko="+ 새 업로드" data-en="+ New Upload">+ 새 업로드</a>
|
||||
|
||||
{% if batch %}
|
||||
<span style="width:1px;height:24px;background:#e2e8f0;margin:0 4px;"></span>
|
||||
<a class="erp-btn {% if active_tab=='work' %}erp-btn-primary{% else %}erp-btn-outline{% endif %}" href="/dispatch/batches/{{ batch.id }}">출고 작업</a>
|
||||
<a class="erp-btn {% if active_tab=='picking' %}erp-btn-primary{% else %}erp-btn-outline{% endif %}" href="/dispatch/batches/{{ batch.id }}/picking">피킹 요약</a>
|
||||
<a class="erp-btn {% if active_tab=='handover' %}erp-btn-primary{% else %}erp-btn-outline{% endif %}" href="/dispatch/batches/{{ batch.id }}/handover">Kagayaku 전달</a>
|
||||
<a class="erp-btn {% if active_tab=='work' %}erp-btn-primary{% else %}erp-btn-outline{% endif %}" href="/dispatch/batches/{{ batch.id }}"
|
||||
data-ko="출고 작업" data-en="Dispatch Work">출고 작업</a>
|
||||
<a class="erp-btn {% if active_tab=='picking' %}erp-btn-primary{% else %}erp-btn-outline{% endif %}" href="/dispatch/batches/{{ batch.id }}/picking"
|
||||
data-ko="피킹 요약" data-en="Picking Summary">피킹 요약</a>
|
||||
<a class="erp-btn {% if active_tab=='handover' %}erp-btn-primary{% else %}erp-btn-outline{% endif %}" href="/dispatch/batches/{{ batch.id }}/handover"
|
||||
data-ko="Kagayaku 전달" data-en="Kagayaku Handover">Kagayaku 전달</a>
|
||||
{% endif %}
|
||||
|
||||
<button id="dsp-lang-btn" type="button" class="erp-btn erp-btn-outline" style="margin-left:auto;font-weight:700;"
|
||||
onclick="dspToggleLang()">EN</button>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// 한글/영문 토글. data-ko/data-en(텍스트), data-ko-ph/data-en-ph(placeholder) 교체.
|
||||
(function () {
|
||||
var KEY = 'dsp_lang';
|
||||
window.dspApplyLang = function (lang) {
|
||||
if (lang !== 'ko' && lang !== 'en') lang = 'ko';
|
||||
document.querySelectorAll('[data-ko]').forEach(function (el) {
|
||||
var v = el.getAttribute('data-' + lang);
|
||||
if (v !== null) el.textContent = v;
|
||||
});
|
||||
document.querySelectorAll('[data-ko-ph]').forEach(function (el) {
|
||||
var v = el.getAttribute('data-' + lang + '-ph');
|
||||
if (v !== null) el.placeholder = v;
|
||||
});
|
||||
var btn = document.getElementById('dsp-lang-btn');
|
||||
if (btn) btn.textContent = (lang === 'ko') ? 'EN' : '한국어';
|
||||
try { localStorage.setItem(KEY, lang); } catch (e) {}
|
||||
document.documentElement.setAttribute('data-dsp-lang', lang);
|
||||
document.dispatchEvent(new CustomEvent('dsp:lang', { detail: { lang: lang } }));
|
||||
};
|
||||
// 현재 언어(동적 텍스트 생성용).
|
||||
window.dspLang = function () {
|
||||
try { return localStorage.getItem(KEY) || 'ko'; } catch (e) { return 'ko'; }
|
||||
};
|
||||
window.dspToggleLang = function () {
|
||||
var cur = 'ko';
|
||||
try { cur = localStorage.getItem(KEY) || 'ko'; } catch (e) {}
|
||||
window.dspApplyLang(cur === 'ko' ? 'en' : 'ko');
|
||||
};
|
||||
var init = 'ko';
|
||||
try { init = localStorage.getItem(KEY) || 'ko'; } catch (e) {}
|
||||
// DOM 준비 후 적용(스크립트가 상단이라 본문 요소가 아직일 수 있어 보강).
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', function () { window.dspApplyLang(init); });
|
||||
} else {
|
||||
window.dspApplyLang(init);
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
|
||||
@@ -6,17 +6,19 @@
|
||||
|
||||
<div class="erp-card">
|
||||
<div class="cpg-card-head" style="display:flex;justify-content:space-between;align-items:center;">
|
||||
<h2>출고 배치 ({{ batches|length }})</h2>
|
||||
<a class="erp-btn erp-btn-primary" href="/dispatch/batches/new">+ 새 업로드</a>
|
||||
<h2><span data-ko="출고 배치" data-en="Batches">출고 배치</span> ({{ batches|length }})</h2>
|
||||
<a class="erp-btn erp-btn-primary" href="/dispatch/batches/new" data-ko="+ 새 업로드" data-en="+ New Upload">+ 새 업로드</a>
|
||||
</div>
|
||||
<div class="erp-table-wrap">
|
||||
<table class="erp-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>날짜</th><th>플랫폼</th><th>배치명</th>
|
||||
<th style="text-align:right">박스</th>
|
||||
<th style="text-align:right">완료</th>
|
||||
<th>생성시각</th><th></th>
|
||||
<th data-ko="날짜" data-en="Date">날짜</th>
|
||||
<th data-ko="플랫폼" data-en="Platform">플랫폼</th>
|
||||
<th data-ko="배치명" data-en="Batch">배치명</th>
|
||||
<th style="text-align:right" data-ko="박스" data-en="Boxes">박스</th>
|
||||
<th style="text-align:right" data-ko="완료" data-en="Done">완료</th>
|
||||
<th data-ko="생성시각" data-en="Created">생성시각</th><th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -33,21 +35,21 @@
|
||||
</td>
|
||||
<td class="erp-muted" style="white-space:nowrap;">{{ b.created_at }}</td>
|
||||
<td style="text-align:right;white-space:nowrap;">
|
||||
<a class="erp-btn erp-btn-outline" href="/dispatch/batches/{{ b.id }}">작업</a>
|
||||
<a class="erp-btn erp-btn-outline" href="/dispatch/batches/{{ b.id }}/picking">피킹</a>
|
||||
<a class="erp-btn erp-btn-outline" href="/dispatch/batches/{{ b.id }}/handover">전달</a>
|
||||
<a class="erp-btn erp-btn-outline" href="/dispatch/batches/{{ b.id }}/download">다운로드</a>
|
||||
<a class="erp-btn erp-btn-outline" href="/dispatch/batches/{{ b.id }}" data-ko="작업" data-en="Work">작업</a>
|
||||
<a class="erp-btn erp-btn-outline" href="/dispatch/batches/{{ b.id }}/picking" data-ko="피킹" data-en="Picking">피킹</a>
|
||||
<a class="erp-btn erp-btn-outline" href="/dispatch/batches/{{ b.id }}/handover" data-ko="전달" data-en="Handover">전달</a>
|
||||
<a class="erp-btn erp-btn-outline" href="/dispatch/batches/{{ b.id }}/download" data-ko="다운로드" data-en="Download">다운로드</a>
|
||||
{% if is_super %}
|
||||
<form method="post" action="/dispatch/batches/{{ b.id }}/delete" style="display:inline;"
|
||||
onsubmit="return confirm('배치 「{{ b.batch_name or '(이름없음)' }}」 와 모든 박스/상품/로그를 삭제합니다. 되돌릴 수 없습니다. 계속할까요?');">
|
||||
<button type="submit" class="erp-btn erp-btn-outline" style="color:#dc2626;border-color:#fca5a5;">삭제</button>
|
||||
<button type="submit" class="erp-btn erp-btn-outline" style="color:#dc2626;border-color:#fca5a5;" data-ko="삭제" data-en="Delete">삭제</button>
|
||||
</form>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% if not batches %}
|
||||
<tr><td colspan="7" class="erp-muted">아직 출고 배치가 없습니다. <a href="/dispatch/batches/new">새 업로드</a>로 시작하세요.</td></tr>
|
||||
<tr><td colspan="7" class="erp-muted" data-ko="아직 출고 배치가 없습니다. 위 + 새 업로드로 시작하세요." data-en="No batches yet. Start with + New Upload above.">아직 출고 배치가 없습니다. 위 + 새 업로드로 시작하세요.</td></tr>
|
||||
{% endif %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@@ -50,22 +50,27 @@
|
||||
|
||||
<div class="erp-card">
|
||||
<div class="cpg-card-head" style="display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;gap:8px;">
|
||||
<h2>출고 작업 — 박스 {{ parcels|length }}개</h2>
|
||||
<span class="erp-muted" id="dsp-done-label">완료 {{ done_count }} / {{ parcels|length }}</span>
|
||||
<h2><span data-ko="출고 작업 · 박스" data-en="Dispatch Work · Boxes">출고 작업 · 박스</span> {{ parcels|length }}</h2>
|
||||
<span class="erp-muted" id="dsp-done-label"
|
||||
data-done="{{ done_count }}" data-total="{{ parcels|length }}">완료 {{ done_count }} / {{ parcels|length }}</span>
|
||||
</div>
|
||||
|
||||
<div class="dsp-toolbar">
|
||||
<div class="dsp-filter" id="dsp-filter">
|
||||
<button class="erp-btn erp-btn-primary" data-filter="all" type="button">전체</button>
|
||||
<button class="erp-btn erp-btn-outline" data-filter="incomplete" type="button">미완료</button>
|
||||
<button class="erp-btn erp-btn-outline" data-filter="label_attached" type="button">라벨부착</button>
|
||||
<button class="erp-btn erp-btn-outline" data-filter="handed_to_kagayaku" type="button">Kagayaku 전달</button>
|
||||
<button class="erp-btn erp-btn-primary" data-filter="all" type="button" data-ko="전체" data-en="All">전체</button>
|
||||
<button class="erp-btn erp-btn-outline" data-filter="incomplete" type="button" data-ko="미완료" data-en="Incomplete">미완료</button>
|
||||
<button class="erp-btn erp-btn-outline" data-filter="label_attached" type="button" data-ko="라벨부착" data-en="Label Attached">라벨부착</button>
|
||||
<button class="erp-btn erp-btn-outline" data-filter="handed_to_kagayaku" type="button" data-ko="Kagayaku 전달" data-en="Kagayaku Handover">Kagayaku 전달</button>
|
||||
</div>
|
||||
<input class="erp-input dsp-search" id="dsp-search" type="search"
|
||||
data-ko-ph="검색: Order ID / Tracking ID / 받는 사람 / SKU"
|
||||
data-en-ph="Search: Order ID / Tracking ID / Recipient / SKU"
|
||||
placeholder="검색: Order ID / Tracking ID / 받는 사람 / SKU" />
|
||||
<div class="dsp-bulk">
|
||||
<button class="dsp-btn-status" type="button" data-bulk="label_attached" onclick="dspBulk(this,'label_attached')">모두 라벨 부착</button>
|
||||
<button class="dsp-btn-status" type="button" data-bulk="handed_to_kagayaku" onclick="dspBulk(this,'handed_to_kagayaku')">모두 Kagayaku 전달</button>
|
||||
<button class="dsp-btn-status" type="button" data-bulk="label_attached" onclick="dspBulk(this,'label_attached')"
|
||||
data-ko="모두 라벨 부착" data-en="Label All">모두 라벨 부착</button>
|
||||
<button class="dsp-btn-status" type="button" data-bulk="handed_to_kagayaku" onclick="dspBulk(this,'handed_to_kagayaku')"
|
||||
data-ko="모두 Kagayaku 전달" data-en="Handover All">모두 Kagayaku 전달</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -101,7 +106,7 @@
|
||||
{% for it in p['items'] %}
|
||||
<li>{{ it.seller_sku }} <span class="qty">× {{ it.quantity }}</span></li>
|
||||
{% else %}
|
||||
<li class="erp-muted" style="font-weight:400;">상품 없음</li>
|
||||
<li class="erp-muted" style="font-weight:400;" data-ko="상품 없음" data-en="No items">상품 없음</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
<div class="dsp-status">
|
||||
@@ -132,7 +137,10 @@
|
||||
var BATCH_ID = document.querySelector('.dsp').getAttribute('data-batch');
|
||||
|
||||
// ── 일괄 토글 처리 (전부 완료면 해제, 아니면 완료) ──
|
||||
var BULK_LABELS = { label_attached: '라벨 부착', handed_to_kagayaku: 'Kagayaku 전달' };
|
||||
var BULK_LABELS = {
|
||||
ko: { label_attached: '라벨 부착', handed_to_kagayaku: 'Kagayaku 전달' },
|
||||
en: { label_attached: 'Label Attached', handed_to_kagayaku: 'Kagayaku Handover' },
|
||||
};
|
||||
|
||||
function allOn(field) {
|
||||
var cards = document.querySelectorAll('.dsp-card');
|
||||
@@ -151,8 +159,12 @@
|
||||
|
||||
window.dspBulk = function (btn, field) {
|
||||
var target = !allOn(field); // 전부 완료면 해제, 아니면 완료
|
||||
var verb = target ? '완료로 표시' : '완료 해제';
|
||||
if (!confirm('이 배치의 모든 박스를 "' + (BULK_LABELS[field] || field) + '" ' + verb + '할까요?')) return;
|
||||
var en = (window.dspLang && window.dspLang() === 'en');
|
||||
var label = BULK_LABELS[en ? 'en' : 'ko'][field] || field;
|
||||
var msg = en
|
||||
? ('Set ALL boxes "' + label + '" to ' + (target ? 'done' : 'not done') + '?')
|
||||
: ('이 배치의 모든 박스를 "' + label + '" ' + (target ? '완료로 표시' : '완료 해제') + '할까요?');
|
||||
if (!confirm(msg)) return;
|
||||
document.querySelectorAll('.dsp-bulk button').forEach(function (b) { b.disabled = true; });
|
||||
var body = new URLSearchParams();
|
||||
body.set('field', field);
|
||||
@@ -207,9 +219,16 @@
|
||||
var cards = document.querySelectorAll('.dsp-card');
|
||||
var done = document.querySelectorAll('.dsp-card.is-done').length;
|
||||
var label = document.getElementById('dsp-done-label');
|
||||
if (label) label.textContent = '완료 ' + done + ' / ' + cards.length;
|
||||
if (!label) return;
|
||||
label.setAttribute('data-done', done);
|
||||
label.setAttribute('data-total', cards.length);
|
||||
var prefix = (window.dspLang && window.dspLang() === 'en') ? 'Done ' : '완료 ';
|
||||
label.textContent = prefix + done + ' / ' + cards.length;
|
||||
}
|
||||
|
||||
// 언어 변경 시 동적 라벨(완료 N/M) 다시 그림.
|
||||
document.addEventListener('dsp:lang', updateDoneLabel);
|
||||
|
||||
// ── 필터 ──
|
||||
var currentFilter = 'all';
|
||||
function matchesFilter(card) {
|
||||
|
||||
@@ -24,24 +24,25 @@
|
||||
{% include "dispatch/_nav.html" %}
|
||||
|
||||
<div class="dsp-noprint" style="margin:8px 0;">
|
||||
<button class="erp-btn erp-btn-primary" type="button" onclick="window.print()">🖨 인쇄하기</button>
|
||||
<button class="erp-btn erp-btn-primary" type="button" onclick="window.print()" data-ko="🖨 인쇄하기" data-en="🖨 Print">🖨 인쇄하기</button>
|
||||
</div>
|
||||
|
||||
<div class="erp-card">
|
||||
<div class="dsp-print-head" style="margin-bottom:10px;">
|
||||
<h2 style="margin:0;">Kagayaku 전달 리스트</h2>
|
||||
<h2 style="margin:0;" data-ko="Kagayaku 전달 리스트" data-en="Kagayaku Handover List">Kagayaku 전달 리스트</h2>
|
||||
</div>
|
||||
<div class="cpg-card-head">
|
||||
<h2>{{ batch.batch_name or 'TikTok 출고' }}</h2>
|
||||
</div>
|
||||
<p class="erp-muted" style="margin:4px 0 4px;">
|
||||
날짜 <b>{{ batch.dispatch_date }}</b> · 플랫폼 <b>{{ batch.platform }}</b>
|
||||
<span data-ko="날짜" data-en="Date">날짜</span> <b>{{ batch.dispatch_date }}</b> ·
|
||||
<span data-ko="플랫폼" data-en="Platform">플랫폼</span> <b>{{ batch.platform }}</b>
|
||||
</p>
|
||||
|
||||
<div class="dsp-ho-grid">
|
||||
<div class="dsp-ho-stat">
|
||||
<div class="n">{{ handover.total_boxes }}</div>
|
||||
<div class="l">총 박스 수</div>
|
||||
<div class="l" data-ko="총 박스 수" data-en="Total Boxes">총 박스 수</div>
|
||||
</div>
|
||||
{% for pv in handover.by_provider %}
|
||||
<div class="dsp-ho-stat">
|
||||
@@ -53,11 +54,12 @@
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<h3 style="margin:16px 0 8px;">Tracking ID 목록 ({{ handover.tracking_list|length }})</h3>
|
||||
<h3 style="margin:16px 0 8px;"><span data-ko="Tracking ID 목록" data-en="Tracking ID List">Tracking ID 목록</span> ({{ handover.tracking_list|length }})</h3>
|
||||
<div class="erp-table-wrap">
|
||||
<table class="erp-table dsp-track">
|
||||
<thead>
|
||||
<tr><th style="text-align:right">No</th><th>Tracking ID</th><th>배송사</th><th>Order ID</th></tr>
|
||||
<tr><th style="text-align:right">No</th><th>Tracking ID</th>
|
||||
<th data-ko="배송사" data-en="Courier">배송사</th><th>Order ID</th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for t in handover.tracking_list %}
|
||||
@@ -69,7 +71,7 @@
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% if not handover.tracking_list %}
|
||||
<tr><td colspan="4" class="erp-muted">전달할 박스가 없습니다.</td></tr>
|
||||
<tr><td colspan="4" class="erp-muted" data-ko="전달할 박스가 없습니다." data-en="No boxes to hand over.">전달할 박스가 없습니다.</td></tr>
|
||||
{% endif %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@@ -5,17 +5,17 @@
|
||||
{% include "dispatch/_nav.html" %}
|
||||
|
||||
<div class="erp-card" style="max-width:640px;">
|
||||
<div class="cpg-card-head"><h2>출고 파일 업로드</h2></div>
|
||||
<div class="cpg-card-head"><h2 data-ko="출고 파일 업로드" data-en="Upload Dispatch Files">출고 파일 업로드</h2></div>
|
||||
<p class="erp-muted" id="dsp-intro" style="margin:4px 0 16px;"></p>
|
||||
|
||||
<form method="post" action="/dispatch/batches" enctype="multipart/form-data" style="display:flex;flex-direction:column;gap:14px;">
|
||||
<label style="display:flex;flex-direction:column;gap:4px;">
|
||||
<span>출고 날짜 <span style="color:#dc2626;">*</span></span>
|
||||
<span><span data-ko="출고 날짜" data-en="Dispatch Date">출고 날짜</span> <span style="color:#dc2626;">*</span></span>
|
||||
<input class="erp-input" type="date" name="dispatch_date" value="{{ today }}" required />
|
||||
</label>
|
||||
|
||||
<label style="display:flex;flex-direction:column;gap:4px;">
|
||||
<span>플랫폼</span>
|
||||
<span data-ko="플랫폼" data-en="Platform">플랫폼</span>
|
||||
<select class="erp-select" name="platform" id="dsp-platform">
|
||||
{% for pf in platforms %}
|
||||
<option value="{{ pf }}">{{ pf }}</option>
|
||||
@@ -24,8 +24,11 @@
|
||||
</label>
|
||||
|
||||
<label style="display:flex;flex-direction:column;gap:4px;">
|
||||
<span>배치명 <span class="erp-muted">(예: 2026-06-19 오전 출고)</span></span>
|
||||
<input class="erp-input" type="text" name="batch_name" placeholder="오전 출고 / 오후 출고 등으로 구분" />
|
||||
<span><span data-ko="배치명" data-en="Batch name">배치명</span>
|
||||
<span class="erp-muted" data-ko="(예: 2026-06-19 오전 출고)" data-en="(e.g. 2026-06-19 AM)">(예: 2026-06-19 오전 출고)</span></span>
|
||||
<input class="erp-input" type="text" name="batch_name"
|
||||
data-ko-ph="오전 출고 / 오후 출고 등으로 구분" data-en-ph="e.g. AM dispatch / PM dispatch"
|
||||
placeholder="오전 출고 / 오후 출고 등으로 구분" />
|
||||
</label>
|
||||
|
||||
<label style="display:flex;flex-direction:column;gap:4px;">
|
||||
@@ -40,8 +43,8 @@
|
||||
</label>
|
||||
|
||||
<div style="display:flex;gap:8px;margin-top:8px;">
|
||||
<button class="erp-btn erp-btn-primary" type="submit">업로드 후 배치 생성</button>
|
||||
<a class="erp-btn erp-btn-outline" href="/dispatch/">취소</a>
|
||||
<button class="erp-btn erp-btn-primary" type="submit" data-ko="업로드 후 배치 생성" data-en="Upload & Create">업로드 후 배치 생성</button>
|
||||
<a class="erp-btn erp-btn-outline" href="/dispatch/" data-ko="취소" data-en="Cancel">취소</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
@@ -51,27 +54,43 @@
|
||||
{% block scripts %}
|
||||
<script>
|
||||
(function () {
|
||||
// 플랫폼별 안내/파일 라벨. 데이터 엑셀이 필수, 라벨 PDF 는 선택(보관용).
|
||||
// 플랫폼별 안내/파일 라벨(한/영). 데이터 엑셀 필수, 라벨 PDF 선택(보관·PII 추출용).
|
||||
var CFG = {
|
||||
"TikTok": {
|
||||
intro: "TikTok: Order Export.xlsx 로 출고 리스트가 생성됩니다. 받는 사람 이름/주소는 라벨 PDF 에서 가져오니 PDF 도 함께 올리세요.",
|
||||
label: "📄 Shipping label + Packing slip.pdf <span class='erp-muted'>(받는 사람 이름/주소 추출 · 권장)</span>",
|
||||
data: "📊 TikTok Order Export.xlsx <span style='color:#dc2626;'>*</span> <span class='erp-muted'>(자동 출고 리스트 기준)</span>"
|
||||
ko: {
|
||||
"TikTok": {
|
||||
intro: "TikTok: Order Export.xlsx 로 출고 리스트가 생성됩니다. 받는 사람 이름/주소는 라벨 PDF 에서 가져오니 PDF 도 함께 올리세요.",
|
||||
label: "📄 Shipping label + Packing slip.pdf <span class='erp-muted'>(받는 사람 이름/주소 추출 · 권장)</span>",
|
||||
data: "📊 TikTok Order Export.xlsx <span style='color:#dc2626;'>*</span> <span class='erp-muted'>(자동 출고 리스트 기준)</span>"
|
||||
},
|
||||
"Shopee": {
|
||||
intro: "Shopee: Packing List.Doorstep Delivery.xlsx 로 출고 리스트가 생성됩니다. 받는 사람 이름/주소는 Shopee Seller Centre.pdf 에서 가져오니 PDF 도 함께 올리세요.",
|
||||
label: "📄 Shopee Seller Centre.pdf <span class='erp-muted'>(받는 사람 이름/주소 추출 · 권장)</span>",
|
||||
data: "📊 Packing List.Doorstep Delivery.xlsx <span style='color:#dc2626;'>*</span> <span class='erp-muted'>(자동 출고 리스트 기준)</span>"
|
||||
}
|
||||
},
|
||||
"Shopee": {
|
||||
intro: "Shopee: Packing List.Doorstep Delivery.xlsx 로 출고 리스트가 생성됩니다. 받는 사람 이름/주소는 Shopee Seller Centre.pdf 에서 가져오니 PDF 도 함께 올리세요.",
|
||||
label: "📄 Shopee Seller Centre.pdf <span class='erp-muted'>(받는 사람 이름/주소 추출 · 권장)</span>",
|
||||
data: "📊 Packing List.Doorstep Delivery.xlsx <span style='color:#dc2626;'>*</span> <span class='erp-muted'>(자동 출고 리스트 기준)</span>"
|
||||
en: {
|
||||
"TikTok": {
|
||||
intro: "TikTok: the dispatch list is built from Order Export.xlsx. Recipient name/address come from the label PDF, so please upload the PDF too.",
|
||||
label: "📄 Shipping label + Packing slip.pdf <span class='erp-muted'>(recipient name/address · recommended)</span>",
|
||||
data: "📊 TikTok Order Export.xlsx <span style='color:#dc2626;'>*</span> <span class='erp-muted'>(dispatch list source)</span>"
|
||||
},
|
||||
"Shopee": {
|
||||
intro: "Shopee: the dispatch list is built from Packing List.Doorstep Delivery.xlsx. Recipient name/address come from Shopee Seller Centre.pdf, so please upload the PDF too.",
|
||||
label: "📄 Shopee Seller Centre.pdf <span class='erp-muted'>(recipient name/address · recommended)</span>",
|
||||
data: "📊 Packing List.Doorstep Delivery.xlsx <span style='color:#dc2626;'>*</span> <span class='erp-muted'>(dispatch list source)</span>"
|
||||
}
|
||||
}
|
||||
};
|
||||
var sel = document.getElementById('dsp-platform');
|
||||
function apply() {
|
||||
var c = CFG[sel.value] || CFG["TikTok"];
|
||||
var lang = (window.dspLang && window.dspLang() === 'en') ? 'en' : 'ko';
|
||||
var c = (CFG[lang] && CFG[lang][sel.value]) || CFG[lang]["TikTok"];
|
||||
document.getElementById('dsp-intro').textContent = c.intro;
|
||||
document.getElementById('dsp-label-lbl').innerHTML = c.label;
|
||||
document.getElementById('dsp-data-lbl').innerHTML = c.data;
|
||||
}
|
||||
sel.addEventListener('change', apply);
|
||||
document.addEventListener('dsp:lang', apply); // 언어 토글 시 재적용
|
||||
apply();
|
||||
})();
|
||||
</script>
|
||||
|
||||
@@ -13,25 +13,27 @@
|
||||
|
||||
<div class="erp-card" style="max-width:560px;">
|
||||
<div class="cpg-card-head" style="display:flex;justify-content:space-between;align-items:center;">
|
||||
<h2>피킹 요약 (SKU별 총 수량)</h2>
|
||||
<span class="erp-muted">총 {{ total_qty }}개 · {{ summary|length }} SKU</span>
|
||||
<h2 data-ko="피킹 요약 (SKU별 총 수량)" data-en="Picking Summary (total qty by SKU)">피킹 요약 (SKU별 총 수량)</h2>
|
||||
<span class="erp-muted"><span data-ko="총" data-en="Total">총</span> {{ total_qty }} · {{ summary|length }} SKU</span>
|
||||
</div>
|
||||
<p class="erp-muted" style="margin:4px 0 12px;">이 화면을 보고 먼저 전체 상품을 꺼내세요.</p>
|
||||
<p class="erp-muted" style="margin:4px 0 12px;" data-ko="이 화면을 보고 먼저 전체 상품을 꺼내세요."
|
||||
data-en="Pick all items first using this list.">이 화면을 보고 먼저 전체 상품을 꺼내세요.</p>
|
||||
<div class="erp-table-wrap">
|
||||
<table class="erp-table dsp-pick">
|
||||
<thead>
|
||||
<tr><th>Seller SKU</th><th>상품명</th><th style="text-align:right">수량</th></tr>
|
||||
<tr><th>Seller SKU</th><th data-ko="상품명" data-en="Product">상품명</th>
|
||||
<th style="text-align:right" data-ko="수량" data-en="Qty">수량</th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for r in summary %}
|
||||
<tr>
|
||||
<td class="sku">{{ r.seller_sku }}</td>
|
||||
<td class="erp-muted">{{ r.product_name or '—' }}</td>
|
||||
<td class="qty">{{ r.total_qty }}개</td>
|
||||
<td class="qty">{{ r.total_qty }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% if not summary %}
|
||||
<tr><td colspan="3" class="erp-muted">피킹할 상품이 없습니다.</td></tr>
|
||||
<tr><td colspan="3" class="erp-muted" data-ko="피킹할 상품이 없습니다." data-en="No items to pick.">피킹할 상품이 없습니다.</td></tr>
|
||||
{% endif %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
Reference in New Issue
Block a user