feat(cupang): 발주 업로드 UX 개선 + 3열 프레임 각각 스크롤
- [쿠팡 발주파일 업로드] 라벨 버튼, 파일 선택 즉시 자동 분석(분석 버튼 제거) - 안내 문구와 분석 결과 상세 표시 제거 — 경고만 남김 - ①②③ 카드가 각자 스크롤: 문서 전체는 스크롤되지 않음 · ① 표 머리글 sticky, 행 높이 축소로 품목 더 많이 표시 · ② KPI 요약까지 고정, 아래 목록만 스크롤 · ③ 헤더/센터 선택 고정, 센터 목록만 스크롤 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
{% extends "erp_base.html" %}
|
{% extends "erp_base.html" %}
|
||||||
|
|
||||||
{% block head_extra %}<link rel="stylesheet" href="/static/cupang.css?v=20260901k" />{% endblock %}
|
{% block head_extra %}<link rel="stylesheet" href="/static/cupang.css?v=20260901l" />{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<section class="cpg">
|
<section class="cpg">
|
||||||
@@ -34,19 +34,16 @@
|
|||||||
<div class="erp-card cpg-form-card cpg-calc-card">
|
<div class="erp-card cpg-form-card cpg-calc-card">
|
||||||
<div class="cpg-card-head">
|
<div class="cpg-card-head">
|
||||||
<h2>① 쿠팡 발주 업로드</h2>
|
<h2>① 쿠팡 발주 업로드</h2>
|
||||||
<span class="erp-muted">엑셀 여러 개 선택 가능 · F13 입고예정일의 하루 전 = 출고일</span>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 쿠팡 발주서(xlsx) 업로드 → 센터별 수량 합산 → ②③ 자동 채움 -->
|
<!-- 쿠팡 발주서(xlsx) 업로드 → 센터별 수량 합산 → ②③ 자동 채움 -->
|
||||||
<div class="cpg-po-box">
|
<div class="cpg-po-box">
|
||||||
<div class="cpg-po-row">
|
<div class="cpg-po-row">
|
||||||
|
<label class="erp-btn erp-btn-primary cpg-po-pick" for="cpg-po-file">쿠팡 발주파일 업로드</label>
|
||||||
<input class="cpg-po-file" type="file" id="cpg-po-file"
|
<input class="cpg-po-file" type="file" id="cpg-po-file"
|
||||||
accept=".xlsx,.xlsm" multiple aria-label="쿠팡 발주 엑셀 선택" />
|
accept=".xlsx,.xlsm" multiple aria-label="쿠팡 발주 엑셀 선택" />
|
||||||
<button type="button" class="erp-btn erp-btn-primary" id="cpg-po-run">업로드 분석</button>
|
<span class="erp-muted cpg-po-msg" id="cpg-po-msg"></span>
|
||||||
</div>
|
</div>
|
||||||
<p class="erp-muted cpg-po-msg" id="cpg-po-msg">
|
|
||||||
B열 쿠팡상품코드 · F열 센터 · G열 수량을 22행부터 읽습니다.
|
|
||||||
</p>
|
|
||||||
<div class="cpg-po-result" id="cpg-po-result" hidden></div>
|
<div class="cpg-po-result" id="cpg-po-result" hidden></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -90,6 +87,7 @@
|
|||||||
<div id="cpg-sum-body" hidden>
|
<div id="cpg-sum-body" hidden>
|
||||||
<div class="cpg-sum-kpis" id="cpg-sum-kpis"></div>
|
<div class="cpg-sum-kpis" id="cpg-sum-kpis"></div>
|
||||||
|
|
||||||
|
<div class="cpg-sum-scroll">
|
||||||
<h3 class="cpg-sum-h3">제품별 박스 <span class="erp-muted">잔여 기준</span></h3>
|
<h3 class="cpg-sum-h3">제품별 박스 <span class="erp-muted">잔여 기준</span></h3>
|
||||||
<div class="cpg-sum-prods" id="cpg-sum-prods"></div>
|
<div class="cpg-sum-prods" id="cpg-sum-prods"></div>
|
||||||
|
|
||||||
@@ -97,6 +95,7 @@
|
|||||||
<div class="cpg-sum-boxes" id="cpg-sum-boxes"></div>
|
<div class="cpg-sum-boxes" id="cpg-sum-boxes"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- ③ 센터 분배 — 선택한 센터만 표시 -->
|
<!-- ③ 센터 분배 — 선택한 센터만 표시 -->
|
||||||
<div class="erp-card cpg-form-card cpg-dist-card">
|
<div class="erp-card cpg-form-card cpg-dist-card">
|
||||||
@@ -372,7 +371,6 @@
|
|||||||
// 업로드 → 센터별 수량 합산(서버) → 센터마다 박스 계산 → ②③ 자동 채움.
|
// 업로드 → 센터별 수량 합산(서버) → 센터마다 박스 계산 → ②③ 자동 채움.
|
||||||
// 박스는 센터별로 포장하므로 계산도 센터 단위로 한다(자투리 혼합 박스 포함).
|
// 박스는 센터별로 포장하므로 계산도 센터 단위로 한다(자투리 혼합 박스 포함).
|
||||||
var poFile = document.getElementById("cpg-po-file");
|
var poFile = document.getElementById("cpg-po-file");
|
||||||
var poRun = document.getElementById("cpg-po-run");
|
|
||||||
var poMsg = document.getElementById("cpg-po-msg");
|
var poMsg = document.getElementById("cpg-po-msg");
|
||||||
var poResult = document.getElementById("cpg-po-result");
|
var poResult = document.getElementById("cpg-po-result");
|
||||||
var uploadShipDate = ""; // F13 − 1일 (확정 대화상자 기본값)
|
var uploadShipDate = ""; // F13 − 1일 (확정 대화상자 기본값)
|
||||||
@@ -560,36 +558,17 @@
|
|||||||
return st;
|
return st;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 분석 결과 상세는 화면에 두지 않는다. 놓치면 안 되는 경고만 남긴다.
|
||||||
function renderPoResult(data, skipped) {
|
function renderPoResult(data, skipped) {
|
||||||
if (!poResult) return;
|
if (!poResult) return;
|
||||||
var html = '<div class="cpg-po-sum">' +
|
|
||||||
'<span class="erp-badge erp-badge-inverse">출고일 ' + esc(data.ship_date || "미확인") + "</span>" +
|
|
||||||
'<span class="erp-badge erp-badge-neutral">파일 ' + ((data.files || []).length) + "개</span>" +
|
|
||||||
'<span class="erp-badge erp-badge-neutral">센터 ' + ((data.centers || []).length) + "곳</span>" +
|
|
||||||
"</div>";
|
|
||||||
if ((data.groups || []).length > 1) {
|
|
||||||
html += '<ul class="cpg-po-dates">' + data.groups.map(function (g) {
|
|
||||||
return "<li><b>" + esc(g.ship_date || "날짜 미확인") + "</b>" +
|
|
||||||
"<span>센터 " + ((g.centers || []).length) + "곳 · " + (g.quantity || 0) + "개</span></li>";
|
|
||||||
}).join("") + "</ul>";
|
|
||||||
}
|
|
||||||
html += '<ul class="cpg-po-files">';
|
|
||||||
(data.files || []).forEach(function (f) {
|
|
||||||
html += "<li><span>" + esc(f.filename) + "</span>" +
|
|
||||||
"<em>입고 " + esc(f.arrival_date || "?") + " → 출고 " + esc(f.ship_date || "?") + "</em>" +
|
|
||||||
"<b>" + f.quantity + "개</b></li>";
|
|
||||||
});
|
|
||||||
html += "</ul>";
|
|
||||||
var warns = (data.warnings || []).slice();
|
var warns = (data.warnings || []).slice();
|
||||||
if (skipped && skipped.length) {
|
if (skipped && skipped.length) {
|
||||||
warns.push("등록되지 않은 센터라 자동 배분에서 제외: " + skipped.join(", "));
|
warns.push("등록되지 않은 센터라 자동 배분에서 제외: " + skipped.join(", "));
|
||||||
}
|
}
|
||||||
if (warns.length) {
|
if (!warns.length) { poResult.hidden = true; poResult.innerHTML = ""; return; }
|
||||||
html += '<ul class="cpg-po-warn">' + warns.map(function (w) {
|
poResult.innerHTML = '<ul class="cpg-po-warn">' + warns.map(function (w) {
|
||||||
return "<li>" + esc(w) + "</li>";
|
return "<li>" + esc(w) + "</li>";
|
||||||
}).join("") + "</ul>";
|
}).join("") + "</ul>";
|
||||||
}
|
|
||||||
poResult.innerHTML = html;
|
|
||||||
poResult.hidden = false;
|
poResult.hidden = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -599,8 +578,7 @@
|
|||||||
}
|
}
|
||||||
var fd = new FormData();
|
var fd = new FormData();
|
||||||
Array.prototype.forEach.call(poFile.files, function (f) { fd.append("files", f); });
|
Array.prototype.forEach.call(poFile.files, function (f) { fd.append("files", f); });
|
||||||
poRun.disabled = true;
|
poMsg.textContent = "분석 중… (" + poFile.files.length + "개)";
|
||||||
poMsg.textContent = "분석 중…";
|
|
||||||
fetch("/cupang/api/box-calc/upload", { method: "POST", body: fd })
|
fetch("/cupang/api/box-calc/upload", { method: "POST", body: fd })
|
||||||
.then(function (r) {
|
.then(function (r) {
|
||||||
return r.json().catch(function () { return {}; }).then(function (d) {
|
return r.json().catch(function () { return {}; }).then(function (d) {
|
||||||
@@ -668,11 +646,15 @@
|
|||||||
: "업로드 결과로 계산했습니다. 센터마다 출고방식을 고르면 확정할 수 있습니다.";
|
: "업로드 결과로 계산했습니다. 센터마다 출고방식을 고르면 확정할 수 있습니다.";
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.catch(function (err) { poMsg.textContent = (err && err.message) || "업로드 실패"; })
|
.catch(function (err) { poMsg.textContent = (err && err.message) || "업로드 실패"; });
|
||||||
.then(function () { poRun.disabled = false; });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (poRun) poRun.addEventListener("click", runUpload);
|
// 파일을 고르고 [열기] 를 누르면 곧바로 분석한다(별도 버튼 없음).
|
||||||
|
if (poFile) {
|
||||||
|
poFile.addEventListener("change", function () {
|
||||||
|
if (poFile.files && poFile.files.length) runUpload();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// ── 배분 집계 ─────────────────────────────────────
|
// ── 배분 집계 ─────────────────────────────────────
|
||||||
function allocatedFor(key) {
|
function allocatedFor(key) {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{% extends "erp_base.html" %}
|
{% extends "erp_base.html" %}
|
||||||
|
|
||||||
{% block head_extra %}<link rel="stylesheet" href="/static/cupang.css?v=20260901k" />{% endblock %}
|
{% block head_extra %}<link rel="stylesheet" href="/static/cupang.css?v=20260901l" />{% 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=20260901k" />{% endblock %}
|
{% block head_extra %}<link rel="stylesheet" href="/static/cupang.css?v=20260901l" />{% 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=20260901k" />{% endblock %}
|
{% block head_extra %}<link rel="stylesheet" href="/static/cupang.css?v=20260901l" />{% 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=20260901k" />{% endblock %}
|
{% block head_extra %}<link rel="stylesheet" href="/static/cupang.css?v=20260901l" />{% 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=20260901k" />{% endblock %}
|
{% block head_extra %}<link rel="stylesheet" href="/static/cupang.css?v=20260901l" />{% 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=20260901k" />{% endblock %}
|
{% block head_extra %}<link rel="stylesheet" href="/static/cupang.css?v=20260901l" />{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<section class="cpg">
|
<section class="cpg">
|
||||||
|
|||||||
+48
-2
@@ -601,7 +601,6 @@
|
|||||||
.cpg-calc-card .erp-table-wrap {
|
.cpg-calc-card .erp-table-wrap {
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
max-height: min(58vh, 560px);
|
|
||||||
}
|
}
|
||||||
.cpg-calc-card .cpg-calc-table thead th {
|
.cpg-calc-card .cpg-calc-table thead th {
|
||||||
position: sticky;
|
position: sticky;
|
||||||
@@ -615,7 +614,7 @@
|
|||||||
.cpg-calc-layout { align-items: stretch; }
|
.cpg-calc-layout { align-items: stretch; }
|
||||||
.cpg-calc-layout > .erp-card { display: flex; flex-direction: column; }
|
.cpg-calc-layout > .erp-card { display: flex; flex-direction: column; }
|
||||||
.cpg-calc-card .erp-table-wrap { flex: 1 1 auto; }
|
.cpg-calc-card .erp-table-wrap { flex: 1 1 auto; }
|
||||||
.cpg-calc-sum { overflow: auto; max-height: min(78vh, 900px); }
|
.cpg-calc-sum { overflow: hidden; }
|
||||||
|
|
||||||
/* 제품명 설정 — 수기 추가 폼 */
|
/* 제품명 설정 — 수기 추가 폼 */
|
||||||
.cpg-prod-manual {
|
.cpg-prod-manual {
|
||||||
@@ -1343,3 +1342,50 @@ body.cpg-dragging { cursor: grabbing; user-select: none; }
|
|||||||
padding: 2px 8px; font-size: 12px; line-height: 1.6;
|
padding: 2px 8px; font-size: 12px; line-height: 1.6;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ══════════════════════════════════════════════════════════════
|
||||||
|
박스 계산 3열 — 카드마다 자체 스크롤(문서 전체는 스크롤 안 함)
|
||||||
|
각 카드: 헤더/요약은 고정, 목록만 스크롤
|
||||||
|
══════════════════════════════════════════════════════════════ */
|
||||||
|
.cpg-calc3 { flex: 1 1 auto; min-height: 0; }
|
||||||
|
.cpg-calc3 > .erp-card { min-height: 0; overflow: hidden; }
|
||||||
|
.cpg-calc3 .cpg-card-head,
|
||||||
|
.cpg-calc3 .cpg-po-box,
|
||||||
|
.cpg-calc3 .cpg-sum-kpis,
|
||||||
|
.cpg-calc3 .cpg-center-pick,
|
||||||
|
.cpg-calc3 .cpg-calc-actions,
|
||||||
|
.cpg-calc3 > .erp-card > .cpg-sum-h3 { flex-shrink: 0; }
|
||||||
|
|
||||||
|
/* ① 입력 표 — 표 머리글 고정 + 표만 스크롤 */
|
||||||
|
.cpg-calc-card .erp-table-wrap { flex: 1 1 auto; min-height: 0; }
|
||||||
|
.cpg-calc-card .cpg-calc-table thead th {
|
||||||
|
position: sticky; top: 0; z-index: 2; background: var(--color-canvas-white);
|
||||||
|
}
|
||||||
|
/* 행 높이 축소 — 한 화면에 더 많은 품목 */
|
||||||
|
.cpg-calc-card .cpg-calc-table th,
|
||||||
|
.cpg-calc-card .cpg-calc-table td { padding: 2px 6px; }
|
||||||
|
.cpg-calc-card .cpg-calc-table .erp-select,
|
||||||
|
.cpg-calc-card .cpg-calc-table .erp-input {
|
||||||
|
padding: 2px 6px; font-size: 12px; line-height: 1.5; height: auto;
|
||||||
|
}
|
||||||
|
.cpg-calc-card .cpg-calc-table .cpg-calc-del { padding: 0 6px; font-size: 11px; line-height: 1.6; }
|
||||||
|
|
||||||
|
/* ② 요약 — KPI 까지 고정, 아래 목록만 스크롤 */
|
||||||
|
.cpg-calc-sum #cpg-sum-body { display: flex; flex-direction: column; flex: 1 1 auto; min-height: 0; }
|
||||||
|
.cpg-sum-scroll { flex: 1 1 auto; min-height: 0; overflow-y: auto; padding-right: 2px; }
|
||||||
|
.cpg-sum-scroll > .cpg-sum-h3:first-child { margin-top: 0; }
|
||||||
|
|
||||||
|
/* ③ 센터 분배 — 센터 목록만 스크롤 */
|
||||||
|
.cpg-dist-card .cpg-dist-list { flex: 1 1 auto; min-height: 0; overflow-y: auto; padding-right: 2px; }
|
||||||
|
|
||||||
|
/* 업로드 버튼 — 네이티브 file input 은 숨기고 라벨을 버튼처럼 */
|
||||||
|
.cpg-po-pick { cursor: pointer; }
|
||||||
|
.cpg-po-box .cpg-po-file {
|
||||||
|
position: absolute; width: 1px; height: 1px;
|
||||||
|
padding: 0; margin: -1px; border: 0; overflow: hidden; clip: rect(0 0 0 0);
|
||||||
|
}
|
||||||
|
.cpg-po-row { align-items: center; }
|
||||||
|
.cpg-po-msg { margin: 0; font-size: 12px; }
|
||||||
|
/* 상단 액션/출고일 탭은 줄어들지 않게 (3열 프레임만 남은 높이를 차지) */
|
||||||
|
.cpg > .erp-page-actions,
|
||||||
|
.cpg > .cpg-date-tabs { flex-shrink: 0; }
|
||||||
|
|||||||
Reference in New Issue
Block a user