98 lines
4.9 KiB
HTML
98 lines
4.9 KiB
HTML
|
|
<!DOCTYPE html>
|
|||
|
|
<html lang="zh-CN">
|
|||
|
|
<head>
|
|||
|
|
<meta charset="UTF-8">
|
|||
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|||
|
|
<title>齐套检查 - 智能排产系统</title>
|
|||
|
|
<link rel="stylesheet" href="../css/common.css">
|
|||
|
|
</head>
|
|||
|
|
<body>
|
|||
|
|
<div id="app">
|
|||
|
|
<header class="header" id="header"></header>
|
|||
|
|
<div class="body">
|
|||
|
|
<aside class="sidebar" id="sidebar"></aside>
|
|||
|
|
<main class="main">
|
|||
|
|
<h1 class="page-title">齐套检查</h1>
|
|||
|
|
<div class="toolbar">
|
|||
|
|
<button class="btn btn-primary" onclick="runKitCheck()">重新齐套计算</button>
|
|||
|
|
<select id="statusFilter" class="btn" style="padding:7px 10px" onchange="render()">
|
|||
|
|
<option value="">全部</option><option value="PASSED">齐套</option><option value="PARTIAL">部分齐套</option><option value="FAILED">缺料</option>
|
|||
|
|
</select>
|
|||
|
|
<div class="search-box"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg><input id="searchInput" placeholder="搜索工单/物料" oninput="render()"></div>
|
|||
|
|
</div>
|
|||
|
|
<div class="card">
|
|||
|
|
<div class="card-body table-wrap">
|
|||
|
|
<table id="kitTable">
|
|||
|
|
<thead><tr><th>工单</th><th>产品</th><th>工序</th><th>物料数</th><th>齐套状态</th><th>缺料明细</th><th>在途物料</th><th>操作</th></tr></thead>
|
|||
|
|
<tbody></tbody>
|
|||
|
|
</table>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</main>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<script src="../js/data.js"></script>
|
|||
|
|
<script src="../js/common.js"></script>
|
|||
|
|
<script>
|
|||
|
|
initPage('kitting');
|
|||
|
|
function render() {
|
|||
|
|
const st = document.getElementById('statusFilter').value;
|
|||
|
|
const kw = document.getElementById('searchInput').value.toLowerCase();
|
|||
|
|
const tbody = document.querySelector('#kitTable tbody');
|
|||
|
|
let rows = apsData.workOrders.filter(wo => {
|
|||
|
|
if (st && wo.kitStatus !== st) return false;
|
|||
|
|
const text = (wo.orderNo + ' ' + wo.productName + ' ' + (wo.requiredMaterials.map(m=>m.name).join(' '))).toLowerCase();
|
|||
|
|
return !kw || text.includes(kw);
|
|||
|
|
});
|
|||
|
|
tbody.innerHTML = rows.map(wo => {
|
|||
|
|
const shortage = wo.requiredMaterials.filter(m => m.status === 'FAILED' || m.status === 'PARTIAL');
|
|||
|
|
const inTransit = wo.requiredMaterials.filter(m => {
|
|||
|
|
const mat = apsData.materials.find(x => x.id == m.materialId);
|
|||
|
|
return mat && mat.inTransit > 0;
|
|||
|
|
});
|
|||
|
|
return `<tr>
|
|||
|
|
<td>${wo.orderNo}</td>
|
|||
|
|
<td>${wo.productName} × ${wo.quantity}</td>
|
|||
|
|
<td>${wo.operationName}</td>
|
|||
|
|
<td>${wo.requiredMaterials.length}</td>
|
|||
|
|
<td>${statusBadge(wo.kitStatus, { PASSED:'success', PARTIAL:'warning', FAILED:'danger' })}</td>
|
|||
|
|
<td class="text-muted">${shortage.map(m => `${m.name} 缺 ${Math.max(0, m.required - m.available)}`).join('<br>') || '-'}</td>
|
|||
|
|
<td class="text-muted">${inTransit.map(m => `${m.name} 在途`).join('<br>') || '-'}</td>
|
|||
|
|
<td class="actions"><button class="btn btn-sm" onclick="viewKit(${wo.id})">明细</button></td>
|
|||
|
|
</tr>`;
|
|||
|
|
}).join('') || '<tr><td colspan="8" class="text-center text-muted">无数据</td></tr>';
|
|||
|
|
}
|
|||
|
|
function viewKit(id) {
|
|||
|
|
const wo = findById(apsData.workOrders, id);
|
|||
|
|
const rows = wo.requiredMaterials.map(m => {
|
|||
|
|
const mat = apsData.materials.find(x => x.id == m.materialId);
|
|||
|
|
return `<tr><td>${m.name}</td><td>${m.required}</td><td>${m.available}</td><td>${mat ? mat.inTransit : 0}</td><td>${statusBadge(m.status, { PASSED:'success', PARTIAL:'warning', FAILED:'danger' })}</td></tr>`;
|
|||
|
|
}).join('');
|
|||
|
|
openModal('齐套明细', `<table><thead><tr><th>物料</th><th>需求</th><th>库存</th><th>在途</th><th>状态</th></tr></thead><tbody>${rows}</tbody></table>`);
|
|||
|
|
}
|
|||
|
|
function runKitCheck() {
|
|||
|
|
apsData.workOrders.forEach(wo => {
|
|||
|
|
wo.kitStatus = 'PASSED';
|
|||
|
|
wo.requiredMaterials.forEach(m => {
|
|||
|
|
const mat = apsData.materials.find(x => x.id == m.materialId);
|
|||
|
|
m.available = mat ? mat.stock : 0;
|
|||
|
|
if (m.available >= m.required) m.status = 'PASSED';
|
|||
|
|
else if (m.available + (mat ? mat.inTransit : 0) >= m.required) { m.status = 'PARTIAL'; wo.kitStatus = 'PARTIAL'; }
|
|||
|
|
else { m.status = 'FAILED'; wo.kitStatus = 'FAILED'; }
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
apsData.productionOrders.forEach(po => {
|
|||
|
|
const wos = apsData.workOrders.filter(wo => wo.productionOrderId == po.id);
|
|||
|
|
const statuses = wos.map(wo => wo.kitStatus);
|
|||
|
|
if (statuses.includes('FAILED')) po.materialKitStatus = 'FAILED';
|
|||
|
|
else if (statuses.includes('PARTIAL')) po.materialKitStatus = 'PARTIAL';
|
|||
|
|
else po.materialKitStatus = 'PASSED';
|
|||
|
|
});
|
|||
|
|
apsData.$utils.save(apsData);
|
|||
|
|
showToast('齐套计算完成', '已刷新所有工单齐套状态'); render();
|
|||
|
|
}
|
|||
|
|
render();
|
|||
|
|
</script>
|
|||
|
|
</body>
|
|||
|
|
</html>
|