aps-agent/aps-frontend/pages/work-order-split.html

93 lines
5.2 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!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="batchIssue()">批量下发已拆分工单</button>
</div>
<div class="card">
<div class="card-body table-wrap">
<table id="poTable">
<thead><tr><th><input type="checkbox" id="checkAll" onclick="toggleAll()"></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('work-order-split');
let selectedIds = [];
function render() {
const rows = apsData.productionOrders.filter(po => po.status !== 'CANCELLED');
const tbody = document.querySelector('#poTable tbody');
tbody.innerHTML = rows.map(po => {
const woCount = apsData.workOrders.filter(wo => wo.productionOrderId == po.id).length;
return `<tr>
<td><input type="checkbox" class="row-check" value="${po.id}" ${selectedIds.includes(po.id)?'checked':''} onchange="toggleRow(${po.id})"></td>
<td>${po.orderNo}</td>
<td>${po.productName} × ${po.quantity}</td>
<td>${po.lineName}</td>
<td>${po.plannedStartDate ? po.plannedStartDate.slice(0,16) : ''} ~ ${po.plannedEndDate ? po.plannedEndDate.slice(0,16) : ''}</td>
<td>${woCount}</td>
<td class="actions">
${woCount === 0 ? `<button class="btn btn-sm btn-primary" onclick="splitWo(${po.id})">拆分工单</button>` : '<span class="text-muted">已拆分</span>'}
${woCount > 0 ? `<button class="btn btn-sm btn-success" onclick="issueAll(${po.id})">下发</button>` : ''}
</td>
</tr>`;
}).join('') || '<tr><td colspan="7" class="text-center text-muted">无数据</td></tr>';
}
function toggleRow(id) { selectedIds = selectedIds.includes(id) ? selectedIds.filter(x=>x!==id) : [...selectedIds, id]; render(); }
function toggleAll() { selectedIds = document.getElementById('checkAll').checked ? apsData.productionOrders.map(po=>po.id) : []; render(); }
function splitWo(poId) {
const po = findById(apsData.productionOrders, poId);
const steps = findRoutingSteps(po.productId);
if (!steps.length) return showToast('无法拆分', '产品缺少工艺路线', 'error');
const start = apsData.$utils.parseDate(po.plannedStartDate || apsData.$utils.fmtDateTime(apsData.$utils.today()));
const end = apsData.$utils.parseDate(po.plannedEndDate || apsData.$utils.addMinutes(start, 24*60));
const span = (end - start) / steps.length;
steps.forEach((step, idx) => {
const op = findById(apsData.operations, step.operationId);
const ws = findWorkstationForOperation(po.lineId, step.operationId);
const s = apsData.$utils.addMinutes(start, idx * span);
const e = apsData.$utils.addMinutes(start, (idx + 1) * span);
apsData.workOrders.push({
id: apsData.$utils.nextId('workOrder'), orderNo: po.orderNo + '-' + String(idx+1).padStart(2,'0'), productionOrderId: po.id, productionOrderNo: po.orderNo,
operationId: op.id, operationName: op.name, sequenceNo: step.sequenceNo, productId: po.productId, productName: po.productName, quantity: po.quantity, unit: po.unit,
completedQuantity: 0, lineId: po.lineId, lineName: po.lineName, workstationId: ws ? ws.id : null, workstationName: ws ? ws.name : '-',
plannedStartTime: apsData.$utils.fmtDateTime(s), plannedEndTime: apsData.$utils.fmtDateTime(e), status: 'PENDING', teamId: null, teamName: null,
requiredMaterials: [], requiredEquipment: [], priority: po.priority, isFrozen: false, kitStatus: 'PASSED', progressPercent: 0, conflictCount: 0, note: ''
});
});
apsData.logs.unshift({ id: apsData.$utils.nextId('log'), type: 'INFO', category: 'WO', operationType: 'SPLIT', targetType: 'PRODUCTION_ORDER', targetId: poId, action: '工单拆分', description: `生产订单 ${po.orderNo} 拆分为 ${steps.length} 个工单`, operator: 'admin', createdAt: apsData.$utils.fmtDateTime(new Date()) });
apsData.$utils.save(apsData); showToast('拆分完成', po.orderNo); render();
}
function issueAll(poId) {
let count = 0;
apsData.workOrders.filter(wo => wo.productionOrderId == poId && wo.status === 'PENDING').forEach(wo => { wo.status = 'ISSUED'; wo.isFrozen = true; count++; });
apsData.$utils.save(apsData); showToast('下发完成', `${count} 个工单已下发`); render();
}
function batchIssue() {
let count = 0;
selectedIds.forEach(id => issueAll(id));
selectedIds = []; render();
}
render();
</script>
</body>
</html>