From 9f291398a1f86fece5c3017acb1531a21051f032 Mon Sep 17 00:00:00 2001 From: star <395723461@qq.com> Date: Tue, 22 Sep 2026 13:45:12 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=BA=93=E5=AD=98=E7=9C=8B=E6=9D=BF=20?= =?UTF-8?q?BOM=20=E6=A0=87=E7=AD=BE=E9=A1=B5=E6=96=B0=E5=A2=9E=E3=80=8C?= =?UTF-8?q?=E6=A8=A1=E6=8B=9F=E9=BD=90=E5=A5=97=E3=80=8D=E8=AF=95=E7=AE=97?= =?UTF-8?q?=E6=A8=A1=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - BOM 物料齐套标签页新增模拟齐套工具条: 输入计划生产数量(套/PCS), 点模拟即按 单套用量×数量 算出每颗料总需求用量, 与当前在库对比, 缺口为正的行整行标红并显示缺多少 - 每行新增「需求用量」「缺口」两列(默认隐藏, 模拟时显示); 每棵 BOM 标题出现模拟结论徽章 (模拟齐套 / 模拟缺料 M/N 种); 选"全部 BOM"时对所有 BOM 同时模拟 - 提供「重置」清除模拟结果, 纯前端计算、不改动任何库存数据 - 新增 .sim-bar / .row-sim-danger / .sim-badge 样式 - 教程同步补充模拟齐套章节 --- static/style.css | 8 ++++ templates/dashboard.html | 74 ++++++++++++++++++++++++++++++++++-- 物料入库出库系统_使用教程.md | 6 +++ 3 files changed, 85 insertions(+), 3 deletions(-) diff --git a/static/style.css b/static/style.css index 1bc67d5..3b07a00 100644 --- a/static/style.css +++ b/static/style.css @@ -78,6 +78,14 @@ button:hover, .btn:hover { filter: brightness(.96); } .bom-filter label { margin: 0; white-space: nowrap; } .bom-filter select { width: auto; min-width: 260px; } +/* BOM 模拟齐套模式 */ +.sim-bar { display: flex; align-items: center; gap: 10px; margin: 0 0 16px; flex-wrap: wrap; padding: 10px 12px; background: #f7f9fc; border: 1px solid #e3e8ef; border-radius: 8px; } +.sim-bar label { margin: 0; white-space: nowrap; } +.sim-bar input[type=number] { width: 130px; } +.badge.sim-badge { margin-left: auto; } +tr.row-sim-danger { background: #fff1f0 !important; } +tr.row-sim-danger td { color: #cf1322; } + /* 批量导入窗口(模态) */ .modal-mask { position: fixed; inset: 0; background: rgba(15,23,35,.5); diff --git a/templates/dashboard.html b/templates/dashboard.html index aed06c9..2959fd4 100644 --- a/templates/dashboard.html +++ b/templates/dashboard.html @@ -53,6 +53,16 @@ + +
+ + + 套 / PCS(该 BOM 计划生产数量) + + + +
+ {% for bb in bom_board %}
@@ -61,14 +71,18 @@ {% if bb.status=='danger' %}缺料 {% elif bb.status=='warn' %}部分偏低 {% else %}齐套{% endif %} +
- + + + + {% for it in bb.parts %} - + @@ -79,9 +93,11 @@ {% elif it.status=='warn' %}偏低 {% else %}正常{% endif %} + + {% else %} - + {% endfor %}
物料编号型号 / 规格单位单套用量当前在库状态
物料编号型号 / 规格单位单套用量当前在库状态
{{ it.m.part_number }} {{ it.m.manufacturer or '-' }} {{ it.m.unit }}
该 BOM 暂无组成物料
该 BOM 暂无组成物料
@@ -112,6 +128,58 @@ function filterBom(){ var cnt = document.getElementById('bomCount'); if(cnt) cnt.textContent = (v==='all') ? ('共 ' + cards.length + ' 棵 BOM') : ('已选 1 / ' + cards.length + ' 棵'); } +function fmtNum(n){ + // 去掉多余小数, 保留至多3位 + var r = Math.round(n*1000)/1000; + return r.toLocaleString('zh-CN', {maximumFractionDigits:3}); +} +function simulateBom(){ + var qEl = document.getElementById('simQty'); + var q = parseFloat(qEl.value); + if(!q || q <= 0){ alert('请输入有效的模拟需求量(正整数)'); return; } + var sel = document.getElementById('bomSelect'); + var v = sel ? sel.value : 'all'; + var cards = (v==='all') + ? document.querySelectorAll('#tab-bom .card[data-bid]') + : document.querySelectorAll('#tab-bom .card[data-bid="'+v+'"]'); + var anyShort = false; + cards.forEach(function(card){ + var shortage = 0, total = 0; + card.querySelectorAll('tbody tr[data-qty]').forEach(function(tr){ + var qpu = parseFloat(tr.getAttribute('data-qty')); + var stock = parseFloat(tr.getAttribute('data-stock')); + var need = qpu * q; + var gap = need - stock; + total++; + if(gap > 0){ shortage++; tr.classList.add('row-sim-danger'); } + else { tr.classList.remove('row-sim-danger'); } + var cols = tr.querySelectorAll('.sim-col'); + cols[0].textContent = fmtNum(need); + cols[1].textContent = gap > 0 ? ('缺 ' + fmtNum(gap)) : '充足'; + cols[0].style.display = ''; + cols[1].style.display = ''; + }); + card.querySelectorAll('th.sim-col').forEach(function(th){ th.style.display=''; }); + var badge = document.getElementById('simBadge-'+card.getAttribute('data-bid')); + if(badge){ + badge.style.display = ''; + if(shortage>0){ badge.className='badge sim-badge danger'; badge.textContent='模拟缺料: 缺 '+shortage+'/'+total+' 种'; anyShort=true; } + else { badge.className='badge sim-badge ok'; badge.textContent='模拟齐套: '+total+' 种充足'; } + } + }); + var hint = document.getElementById('simHint'); + if(hint){ + if(cards.length>1) hint.textContent = anyShort ? ('共模拟 '+cards.length+' 棵 BOM,存在缺料') : ('共模拟 '+cards.length+' 棵 BOM,全部齐套'); + else if(cards.length===1) hint.textContent = anyShort ? '该 BOM 模拟缺料' : '该 BOM 模拟齐套'; + } +} +function resetSim(){ + document.querySelectorAll('#tab-bom .sim-col').forEach(function(e){ e.style.display='none'; }); + document.querySelectorAll('#tab-bom tbody tr').forEach(function(tr){ tr.classList.remove('row-sim-danger'); }); + document.querySelectorAll('#tab-bom .sim-badge').forEach(function(b){ b.style.display='none'; }); + var q = document.getElementById('simQty'); if(q) q.value=''; + var h = document.getElementById('simHint'); if(h) h.textContent=''; +} filterBom(); {% endblock %} diff --git a/物料入库出库系统_使用教程.md b/物料入库出库系统_使用教程.md index 09a472c..9d2289c 100644 --- a/物料入库出库系统_使用教程.md +++ b/物料入库出库系统_使用教程.md @@ -79,6 +79,12 @@ > **BOM 物料齐套标签页**:看板顶部有「📦 库存预警 / 🔧 BOM 物料齐套」两个标签(仅管理员、主管可见)。切到 **BOM 物料齐套** 后,顶部有一个「选择 BOM」下拉框:默认「全部 BOM(总览)」会逐棵列出其组成物料的「单套用量 / 当前在库 / 状态」;选中某棵 BOM 则只看那棵。右侧显示「共 N 棵 / 已选 1/N 棵」计数。每棵 BOM 标题处给出整体齐套结论(齐套 / 部分偏低 / 缺料),点物料编号可跳到该料明细。这样不用进 BOM 列表就能在看板上直接看清每棵 BOM 里每颗料的库存情况。 +> **模拟齐套(试算缺料)**:在 BOM 物料齐套标签页里,「选择 BOM」下拉框下方有一条「模拟齐套」工具条。输入该 BOM 的**计划生产数量**(如 1000 套 / PCS),点「▶ 模拟齐套」,系统按每颗料的「单套用量 × 计划数量」算出**总需求用量**,与**当前在库**对比: +> - 每行右侧新增「需求用量」「缺口」两列;缺口为正的行(需求 > 在库)整行标红并显示「缺 X」; +> - 该 BOM 标题处出现模拟结论徽章:「模拟齐套 / N 种充足」或「模拟缺料 / 缺 M/N 种」; +> - 选择「全部 BOM(总览)」时会对每棵 BOM 同时模拟,工具条右侧给出整体结论。 +> 点「重置」可清除模拟结果。该模式**只做试算、不改任何库存数据**,适合接单前快速判断"这批货能不能齐套、还差多少料"。 + --- ## 五、物料管理