109 lines
6.5 KiB
HTML
109 lines
6.5 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">
|
||
<select id="statusFilter" class="btn" style="padding:7px 10px" onchange="render()">
|
||
<option value="">全部状态</option>
|
||
<option value="DRAFT">草稿</option>
|
||
<option value="CONFIRMED">已确认</option>
|
||
<option value="ISSUED">已下发</option>
|
||
<option value="IN_PROGRESS">执行中</option>
|
||
<option value="COMPLETED">已完成</option>
|
||
<option value="CLOSED">已关闭</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="poTable">
|
||
<thead><tr><th>生产订单</th><th>销售订单</th><th>产品</th><th>数量</th><th>产线</th><th>计划起止</th><th>状态</th><th>冲突</th><th>操作</th></tr></thead>
|
||
<tbody></tbody>
|
||
</table>
|
||
</div>
|
||
<div class="card-footer flex justify-between">
|
||
<span id="totalInfo" class="text-muted"></span>
|
||
<div id="pagination" class="pagination"></div>
|
||
</div>
|
||
</div>
|
||
</main>
|
||
</div>
|
||
</div>
|
||
<script src="../js/data.js"></script>
|
||
<script src="../js/common.js"></script>
|
||
<script>
|
||
initPage('production-orders');
|
||
let page = 1, pageSize = 10;
|
||
function render(pg) {
|
||
if (pg) page = pg;
|
||
const st = document.getElementById('statusFilter').value;
|
||
const kw = document.getElementById('searchInput').value.toLowerCase();
|
||
let rows = apsData.productionOrders.filter(po => {
|
||
if (st && po.status !== st) return false;
|
||
const text = (po.orderNo + ' ' + po.productName + ' ' + po.salesOrderNo).toLowerCase();
|
||
return !kw || text.includes(kw);
|
||
}).sort((a, b) => b.id - a.id);
|
||
const total = rows.length;
|
||
rows = rows.slice((page - 1) * pageSize, page * pageSize);
|
||
const tbody = document.querySelector('#poTable tbody');
|
||
tbody.innerHTML = rows.map(po => `
|
||
<tr>
|
||
<td>${po.orderNo}</td>
|
||
<td><a href="orders.html">${po.salesOrderNo}</a></td>
|
||
<td>${po.productName}</td>
|
||
<td>${po.quantity}</td>
|
||
<td>${po.lineName}</td>
|
||
<td>${po.plannedStartDate ? po.plannedStartDate.slice(0,10) : ''} ~ ${po.plannedEndDate ? po.plannedEndDate.slice(0,10) : ''}</td>
|
||
<td>${statusBadge(po.status, { DRAFT:'default', CONFIRMED:'primary', ISSUED:'warning', IN_PROGRESS:'warning', COMPLETED:'success', CLOSED:'default', CANCELLED:'danger' })}</td>
|
||
<td>${po.conflictCount || 0}</td>
|
||
<td class="actions">
|
||
<button class="btn btn-sm" onclick="viewPo(${po.id})">详情</button>
|
||
${po.status === 'DRAFT' ? `<button class="btn btn-sm btn-success" onclick="confirmPo(${po.id})">确认</button>` : ''}
|
||
${po.status === 'CONFIRMED' ? `<button class="btn btn-sm btn-danger" onclick="revokePo(${po.id})">撤销</button>` : ''}
|
||
</td>
|
||
</tr>`).join('') || '<tr><td colspan="9" class="text-center text-muted">无数据</td></tr>';
|
||
document.getElementById('totalInfo').textContent = `共 ${total} 条`;
|
||
renderPagination('pagination', page, total, pageSize, 'render');
|
||
}
|
||
|
||
function viewPo(id) {
|
||
const po = findById(apsData.productionOrders, id);
|
||
const wos = apsData.workOrders.filter(wo => wo.productionOrderId == id).map(wo => `<li>${wo.orderNo} ${wo.operationName} ${wo.plannedStartTime.slice(0,16)} ~ ${wo.plannedEndTime.slice(0,16)}</li>`).join('');
|
||
openModal('生产订单详情', `
|
||
<div class="form-row"><div class="form-group"><label>生产订单号</label><input value="${po.orderNo}" readonly></div><div class="form-group"><label>销售订单</label><input value="${po.salesOrderNo}" readonly></div></div>
|
||
<div class="form-row"><div class="form-group"><label>产品</label><input value="${po.productName} × ${po.quantity}" readonly></div><div class="form-group"><label>产线</label><input value="${po.lineName}" readonly></div></div>
|
||
<div class="form-row"><div class="form-group"><label>计划开始</label><input value="${po.plannedStartDate}" readonly></div><div class="form-group"><label>计划结束</label><input value="${po.plannedEndDate}" readonly></div></div>
|
||
<div class="form-row"><div class="form-group"><label>排产引擎</label><input value="${po.schedulingEngine}" readonly></div><div class="form-group"><label>约束状态</label><input value="${po.constraintCheckStatus}" readonly></div></div>
|
||
<p><strong>工单清单:</strong></p><ul>${wos}</ul>
|
||
`, '', true);
|
||
}
|
||
function confirmPo(id) {
|
||
const po = findById(apsData.productionOrders, id); po.status = 'CONFIRMED';
|
||
apsData.logs.unshift({ id: apsData.$utils.nextId('log'), type: 'INFO', category: 'ORDER', operationType: 'CONFIRM', targetType: 'PRODUCTION_ORDER', targetId: id, action: '确认生产订单', description: `生产订单 ${po.orderNo} 已确认`, operator: 'admin', createdAt: apsData.$utils.fmtDateTime(new Date()) });
|
||
apsData.$utils.save(apsData); showToast('确认成功', po.orderNo); render();
|
||
}
|
||
function revokePo(id) {
|
||
confirmAction('撤销后资源将释放,确定吗?', `revokePoExec(${id})`);
|
||
}
|
||
function revokePoExec(id) {
|
||
const po = findById(apsData.productionOrders, id); po.status = 'CANCELLED';
|
||
apsData.workOrders.filter(wo => wo.productionOrderId == id).forEach(wo => wo.status = 'CANCELLED');
|
||
apsData.logs.unshift({ id: apsData.$utils.nextId('log'), type: 'WARNING', category: 'ORDER', operationType: 'CANCEL', targetType: 'PRODUCTION_ORDER', targetId: id, action: '撤销生产订单', description: `生产订单 ${po.orderNo} 已撤销`, operator: 'admin', createdAt: apsData.$utils.fmtDateTime(new Date()) });
|
||
apsData.$utils.save(apsData); showToast('撤销成功', po.orderNo); render();
|
||
}
|
||
render();
|
||
</script>
|
||
</body>
|
||
</html>
|