aps-agent/aps-frontend/pages/order-audit.html

117 lines
8.8 KiB
HTML
Raw Permalink 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>
<p class="page-desc">对待审核订单进行审批,系统根据客户等级、交期、金额和手工优先级自动计算优先级评分。</p>
<div class="toolbar">
<button class="btn" onclick="priorityConfigModal()">优先级权重</button>
<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="auditTable">
<thead><tr><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('order-audit');
function levelScore(level) { return { VIP: 10, A: 8, B: 5, C: 3 }[level] || 3; }
function urgencyScore(deliveryDate) {
const days = Math.ceil((apsData.$utils.parseDate(deliveryDate + ' 00:00') - apsData.$utils.today()) / (24 * 60 * 60 * 1000));
if (days <= 3) return 10; if (days <= 7) return 7; if (days <= 14) return 5; return 3;
}
function amountScore(amount) { return Math.min(10, Math.round(amount / 50000)); }
function computeScore(o, manual) {
const w = apsData.scheduleParams.priorityWeights || { customer_level_weight: 0.30, urgency_weight: 0.35, amount_weight: 0.15, manual_weight: 0.20 };
return (w.customer_level_weight * levelScore(o.customerLevel) + w.urgency_weight * urgencyScore(o.deliveryDate) + w.amount_weight * amountScore(o.totalAmount) + w.manual_weight * (manual || o.manualPriority || 5)).toFixed(2);
}
function render() {
const kw = document.getElementById('searchInput').value.toLowerCase();
const rows = apsData.salesOrders.filter(o => o.status === 'SUBMITTED').filter(o => {
return !kw || (o.orderNo + ' ' + o.customerName + ' ' + o.items[0].productName).toLowerCase().includes(kw);
}).sort((a, b) => a.priority - b.priority);
const tbody = document.querySelector('#auditTable tbody');
tbody.innerHTML = rows.map(o => {
const item = o.items[0];
return `<tr>
<td>${o.orderNo}</td>
<td>${o.customerName} <span class="badge badge-${o.customerLevel==='VIP'?'danger':(o.customerLevel==='A'?'primary':'default')}">${o.customerLevel}</span></td>
<td>${item.productName} × ${item.quantity}</td>
<td>${o.deliveryDate}</td>
<td>¥${o.totalAmount.toLocaleString()}</td>
<td class="text-primary"><strong>${computeScore(o)}</strong></td>
<td class="actions"><button class="btn btn-sm btn-success" onclick="auditOrder(${o.id})">审核</button></td>
</tr>`;
}).join('') || '<tr><td colspan="7" class="text-center text-muted">暂无待审核订单</td></tr>';
}
function auditOrder(id) {
const o = findById(apsData.salesOrders, id);
openModal('订单审核', `
<div class="form-row"><div class="form-group"><label>订单号</label><input value="${o.orderNo}" readonly></div><div class="form-group"><label>客户等级</label><input value="${o.customerLevel}" readonly></div></div>
<div class="form-row"><div class="form-group"><label>产品/数量</label><input value="${o.items[0].productName} × ${o.items[0].quantity}" readonly></div><div class="form-group"><label>订单金额</label><input value="${o.totalAmount.toLocaleString()}" readonly></div></div>
<div class="form-row"><div class="form-group"><label>交期</label><input value="${o.deliveryDate}" readonly></div><div class="form-group"><label>优先级 (1-10)</label><input id="a_priority" type="number" min="1" max="10" value="${o.priority}" oninput="updateScore(${id})"></div></div>
<div class="card mt-2" style="padding:12px">
<div class="flex justify-between"><span>客户等级得分</span><strong>${levelScore(o.customerLevel)}</strong></div>
<div class="flex justify-between"><span>交期紧迫度得分</span><strong>${urgencyScore(o.deliveryDate)}</strong></div>
<div class="flex justify-between"><span>订单金额得分</span><strong>${amountScore(o.totalAmount)}</strong></div>
<div class="flex justify-between mt-2 text-primary"><span>综合优先级评分</span><strong id="a_score">${computeScore(o)}</strong></div>
</div>
<div class="form-row mt-2"><div class="form-group"><label>审核意见 / 退回原因</label><textarea id="a_reason" rows="2"></textarea></div></div>
`, `<button class="btn" onclick="closeModal()">取消</button><button class="btn btn-danger" onclick="rejectOrder(${id})">退回</button><button class="btn btn-success" onclick="approveOrder(${id})">通过</button>`);
}
function updateScore(id) {
const o = findById(apsData.salesOrders, id);
document.getElementById('a_score').textContent = computeScore(o, Number(document.getElementById('a_priority').value));
}
function approveOrder(id) {
const o = findById(apsData.salesOrders, id);
o.priority = Number(document.getElementById('a_priority').value);
o.manualPriority = o.priority;
o.status = 'CONFIRMED';
o.updatedAt = apsData.$utils.fmtDateTime(new Date());
o.changes.push({ id: apsData.$utils.nextId('orderChange'), changeType: 'AUDIT', oldValue: 'SUBMITTED', newValue: 'CONFIRMED', reason: '审核通过', operator: 'admin', createdAt: apsData.$utils.fmtDateTime(new Date()) });
apsData.logs.unshift({ id: apsData.$utils.nextId('log'), type: 'INFO', category: 'ORDER', operationType: 'AUDIT', targetType: 'SALES_ORDER', targetId: id, action: '审核通过', description: `订单 ${o.orderNo} 审核通过`, operator: 'admin', createdAt: apsData.$utils.fmtDateTime(new Date()) });
apsData.$utils.save(apsData); closeModal(); showToast('审核通过'); render();
}
function rejectOrder(id) {
const o = findById(apsData.salesOrders, id);
const reason = document.getElementById('a_reason').value || '审核不通过';
o.status = 'REJECTED';
o.updatedAt = apsData.$utils.fmtDateTime(new Date());
o.changes.push({ id: apsData.$utils.nextId('orderChange'), changeType: 'AUDIT', oldValue: 'SUBMITTED', newValue: 'REJECTED', reason, operator: 'admin', createdAt: apsData.$utils.fmtDateTime(new Date()) });
apsData.logs.unshift({ id: apsData.$utils.nextId('log'), type: 'WARNING', category: 'ORDER', operationType: 'AUDIT', targetType: 'SALES_ORDER', targetId: id, action: '审核退回', description: `订单 ${o.orderNo} 被退回:${reason}`, operator: 'admin', createdAt: apsData.$utils.fmtDateTime(new Date()) });
apsData.$utils.save(apsData); closeModal(); showToast('已退回', reason); render();
}
function priorityConfigModal() {
const w = apsData.scheduleParams.priorityWeights || { customer_level_weight: 0.30, urgency_weight: 0.35, amount_weight: 0.15, manual_weight: 0.20 };
openModal('优先级权重配置', `
<div class="form-row"><div class="form-group"><label>客户等级权重</label><input id="w_cust" type="number" step="0.05" value="${w.customer_level_weight}"></div><div class="form-group"><label>交期紧迫度权重</label><input id="w_urg" type="number" step="0.05" value="${w.urgency_weight}"></div></div>
<div class="form-row"><div class="form-group"><label>订单金额权重</label><input id="w_amt" type="number" step="0.05" value="${w.amount_weight}"></div><div class="form-group"><label>手工优先级权重</label><input id="w_man" type="number" step="0.05" value="${w.manual_weight}"></div></div>
`, `<button class="btn" onclick="closeModal()">取消</button><button class="btn btn-primary" onclick="saveWeights()">保存</button>`);
}
function saveWeights() {
apsData.scheduleParams.priorityWeights = { customer_level_weight: Number(document.getElementById('w_cust').value), urgency_weight: Number(document.getElementById('w_urg').value), amount_weight: Number(document.getElementById('w_amt').value), manual_weight: Number(document.getElementById('w_man').value) };
apsData.$utils.save(apsData); closeModal(); showToast('权重已更新'); render();
}
render();
</script>
</body>
</html>