<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>缠布缺件排产</title>
<script src="https://cdn.jsdelivr.net/npm/xlsx@0.18.5/dist/xlsx.full.min.js"></script>
<style>
body {
font-family: Arial, sans-serif;
margin: 20px;
line-height: 1.6;
}
.container {
max-width: 1200px;
margin: 0 auto;
}
h1, h2 {
color: #333;
}
.section {
margin-bottom: 30px;
padding: 20px;
border: 1px solid #ddd;
border-radius: 5px;
background-color: #f9f9f9;
}
table {
width: 100%;
border-collapse: collapse;
margin-top: 15px;
}
th, td {
border: 1px solid #ddd;
padding: 8px;
text-align: left;
}
th {
background-color: #f2f2f2;
}
tr:nth-child(even) {
background-color: #f9f9f9;
}
.warning {
color: #d9534f;
font-weight: bold;
}
.input-group {
margin-bottom: 15px;
}
label {
display: inline-block;
width: 200px;
margin-right: 10px;
}
input, button {
padding: 8px;
margin-right: 10px;
}
button {
background-color: #4CAF50;
color: white;
border: none;
cursor: pointer;
border-radius: 4px;
}
button:hover {
background-color: #45a049;
}
#fileInput {
display: none;
}
.file-upload {
display: inline-block;
padding: 8px 12px;
background: #337ab7;
color: white;
border-radius: 4px;
cursor: pointer;
}
.file-upload:hover {
background: #286090;
}
#loading {
display: none;
margin-left: 10px;
color: #337ab7;
font-weight: bold;
}
.summary {
margin-top: 15px;
padding: 10px;
background-color: #e7f3fe;
border-left: 5px solid #2196F3;
}
.stats {
margin-top: 10px;
font-weight: bold;
}
.on-time {
color: #5cb85c;
}
.late {
color: #d9534f;
}
</style>
</head>
<body>
<div>
<h1>缠布缺件排产</h1>
<div>
<h2>产能设置</h2>
<div>
<label for="workers">每日生产人数:</label>
<input type="number" id="workers" value="3" min="1">
</div>
<div>
<label for="productivity">每人每日产量:</label>
<input type="number" id="productivity" value="70" min="1">
</div>
<div>
<label for="startDate">排产开始日期:</label>
<input type="date" id="startDate">
</div>
</div>
<div>
<h2>数据导入</h2>
<div>
<label>Excel数据导入:</label>
<label for="fileInput">选择Excel文件</label>
<input type="file" id="fileInput" accept=".xlsx, .xls" onchange="handleFileUpload(this.files)">
<span id="fileName" style="margin-left:10px;"></span>
</div>
<div>
<label>Excel格式要求:</label>
<span>第一列:产品编号, 第二列:交付日期, 第三列:缺件数量</span>
</div>
</div>
<div>
<h2>原始数据</h2>
<div id="rawDataSummary"></div>
<table id="rawDataTable">
<thead>
<tr>
<th>产品编号</th>
<th>交期处理(提前一天)</th>
<th>缺件数量</th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
<div>
<h2>排产计划</h2>
<button onclick="generateSchedule()" id="generateBtn">生成排产计划</button>
<span id="loading">正在计算中,请稍候...</span>
<div id="scheduleSummary"></div>
<div id="scheduleStats"></div>
<table id="scheduleTable">
<thead>
<tr>
<th>产品编号</th>
<th>缺件数量</th>
<th>交付日期</th>
<th>排产日期</th>
<th>是否超期</th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
<div>
<h2>预警清单</h2>
<div id="warningSummary"></div>
<table id="warningTable">
<thead>
<tr>
<th>产品编号</th>
<th>缺件数量</th>
<th>交付日期</th>
<th>预计排产日期</th>
<th>超期天数</th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
</div>
<script>
// 全局变量
let rawData = [];
let scheduleData = [];
let warningData = [];
// 页面加载时设置默认日期为今天
window.onload = function() {
const today = new Date();
const formattedDate = today.toISOString().split('T')[0];
document.getElementById('startDate').value = formattedDate;
};
// 解析Excel日期数字
function parseExcelDate(excelDate) {
const utcDays = Math.floor(excelDate - 25569);
const utcValue = utcDays * 86400 * 1000;
let date = new Date(utcValue);
if (excelDate >= 60) {
date.setTime(date.getTime() - 86400 * 1000);
}
return date.toISOString().split('T')[0];
}
// 处理Excel文件上传
function handleFileUpload(files) {
if (files.length === 0) return;
const file = files[0];
document.getElementById('fileName').textContent = file.name;
const reader = new FileReader();
reader.onload = function(e) {
try {
const data = new Uint8Array(e.target.result);
const workbook = XLSX.read(data, { type: 'array' });
const firstSheet = workbook.Sheets[workbook.SheetNames[0]];
const jsonData = XLSX.utils.sheet_to_json(firstSheet, { header: 1 });
rawData = [];
for (let i = 1; i < jsonData.length; i++) {
if (jsonData[i].length >= 3) {
let deliveryDate = '';
const dateValue = jsonData[i][1];
if (typeof dateValue === 'number') {
deliveryDate = parseExcelDate(dateValue);
} else if (dateValue instanceof Date) {
deliveryDate = dateValue.toISOString().split('T')[0];
} else if (typeof dateValue === 'string') {
const parsedDate = new Date(dateValue);
if (!isNaN(parsedDate.getTime())) {
deliveryDate = parsedDate.toISOString().split('T')[0];
}
}
let quantity = 0;
if (typeof jsonData[i][2] === 'number') {
quantity = Math.floor(jsonData[i][2]);
} else if (typeof jsonData[i][2] === 'string') {
quantity = parseInt(jsonData[i][2]) || 0;
}
rawData.push({
productId: String(jsonData[i][0] || ''),
deliveryDate: deliveryDate,
quantity: quantity,
originalQuantity: quantity // 保存原始数量用于显示
});
}
}
console.log("解析后的数据:", rawData);
displayRawData();
updateRawDataSummary();
} catch (error) {
console.error("解析Excel文件时出错:", error);
alert('解析Excel文件时出错: ' + error.message);
}
};
reader.onerror = function() {
alert('读取文件时出错');
};
reader.readAsArrayBuffer(file);
}
// 显示原始数据
function displayRawData() {
const tbody = document.querySelector('#rawDataTable tbody');
tbody.innerHTML = '';
rawData.forEach(item => {
const row = document.createElement('tr');
row.innerHTML = `
<td>${item.productId}</td>
<td>${item.deliveryDate}</td>
<td>${item.originalQuantity}</td>
`;
tbody.appendChild(row);
});
}
// 更新原始数据摘要
function updateRawDataSummary() {
const summary = document.getElementById('rawDataSummary');
const totalItems = rawData.length;
const totalQuantity = rawData.reduce((sum, item) => sum + item.originalQuantity, 0);
summary.innerHTML = `
共 ${totalItems} 条记录,总缺件数量: ${totalQuantity} 件
${rawData.some(item => !item.deliveryDate) ? '<span> (警告: 部分记录缺少交付日期)</span>' : ''}
`;
}
// 生成排产计划(精确交付版)
function generateSchedule() {
const btn = document.getElementById('generateBtn');
const loading = document.getElementById('loading');
btn.disabled = true;
loading.style.display = 'inline';
setTimeout(() => {
try {
console.log("开始生成精确交付排产计划...");
// 验证数据
if (rawData.length === 0) {
alert('请先导入Excel数据');
return;
}
const invalidData = rawData.filter(item =>
!item.deliveryDate || isNaN(new Date(item.deliveryDate).getTime()) || isNaN(item.originalQuantity)
);
if (invalidData.length > 0) {
console.error("无效数据:", invalidData);
alert(`发现 ${invalidData.length} 条无效记录,请检查数据`);
return;
}
// 获取产能参数
const workers = parseInt(document.getElementById('workers').value) || 3;
const productivity = parseInt(document.getElementById('productivity').value) || 70;
const startDateStr = document.getElementById('startDate').value;
if (!startDateStr) {
alert('请设置排产开始日期');
return;
}
const dailyCapacity = workers * productivity;
scheduleData = [];
warningData = [];
// 1. 按交付日期升序排序,数量小的优先
const sortedData = [...rawData].sort((a, b) => {
// 先按交付日期排序
const dateDiff = new Date(a.deliveryDate) - new Date(b.deliveryDate);
if (dateDiff !== 0) return dateDiff;
// 同一天交付的,数量小的优先
return a.originalQuantity - b.originalQuantity;
});
console.log("排序后的数据:", sortedData);
// 2. 初始化生产日历
let currentDate = new Date(startDateStr);
let dailyRemaining = dailyCapacity;
// 3. 逐个处理每个订单(即使产品相同也单独处理)
for (const item of sortedData) {
// 重置数量为原始值(因为前面的处理可能修改了quantity)
item.quantity = item.originalQuantity;
let productionDate = new Date(currentDate);
let quantityRemaining = item.quantity;
let isLate = false;
// 分配生产日期
while (quantityRemaining > 0) {
if (dailyRemaining === 0) {
// 转到下一天
currentDate.setDate(currentDate.getDate() + 1);
dailyRemaining = dailyCapacity;
productionDate = new Date(currentDate);
}
const allocate = Math.min(quantityRemaining, dailyRemaining);
// 检查是否超期(生产日期 > 交付日期)
const currentIsLate = productionDate > new Date(item.deliveryDate);
isLate = isLate || currentIsLate;
scheduleData.push({
productId: item.productId,
quantity: allocate,
deliveryDate: item.deliveryDate,
productionDate: productionDate.toISOString().split('T')[0],
isLate: currentIsLate
});
quantityRemaining -= allocate;
dailyRemaining -= allocate;
}
// 如果整个订单超期,添加到预警
if (isLate) {
const lastProdDate = new Date(
scheduleData.filter(x =>
x.productId === item.productId &&
x.deliveryDate === item.deliveryDate
)
.reduce((latest, curr) =>
new Date(curr.productionDate) > new Date(latest.productionDate) ? curr : latest
).productionDate
);
const lateDays = Math.ceil(
(lastProdDate - new Date(item.deliveryDate)) /
(1000 * 60 * 60 * 24)
);
warningData.push({
productId: item.productId,
quantity: item.originalQuantity,
deliveryDate: item.deliveryDate,
productionDate: lastProdDate.toISOString().split('T')[0],
lateDays: lateDays
});
}
}
// 4. 显示结果
displaySchedule();
displayWarnings();
updateSummary();
console.log("精确交付排产计划生成完成");
console.log("排产数据:", scheduleData);
console.log("预警数据:", warningData);
} catch (error) {
console.error("生成排产计划时出错:", error);
alert('生成排产计划时出错: ' + error.message);
} finally {
btn.disabled = false;
loading.style.display = 'none';
}
}, 100);
}
// 更新摘要信息
function updateSummary() {
const scheduleSummary = document.getElementById('scheduleSummary');
const warningSummary = document.getElementById('warningSummary');
const statsElement = document.getElementById('scheduleStats');
const totalOrders = rawData.length;
const totalQuantity = rawData.reduce((sum, item) => sum + item.originalQuantity, 0);
const lateOrders = warningData.length;
scheduleSummary.innerHTML = `
共 ${totalOrders} 个订单需要生产,总数量: ${totalQuantity} 件
`;
const warningQuantity = warningData.reduce((sum, item) => sum + item.quantity, 0);
warningSummary.innerHTML = `
共 ${lateOrders} 个订单需要预警,总数量: ${warningQuantity} 件
`;
// 计算按时完成率
const onTimeRate = totalOrders > 0 ? ((totalOrders - lateOrders) / totalOrders * 100).toFixed(2) : 0;
statsElement.innerHTML = `
<span>按时完成率: ${onTimeRate}% (${totalOrders - lateOrders}/${totalOrders})</span> |
<span>超期订单: ${lateOrders}</span>
`;
}
// 显示排产计划
function displaySchedule() {
const tbody = document.querySelector('#scheduleTable tbody');
tbody.innerHTML = '';
// 按交付日期分组显示
const deliveryGroups = [...new Set(rawData.map(item => item.deliveryDate))].sort((a, b) =>
new Date(a) - new Date(b)
);
for (const deliveryDate of deliveryGroups) {
// 获取该交付日期的所有订单
const orders = rawData.filter(item => item.deliveryDate === deliveryDate);
// 检查该交付日期是否有超期订单
const hasLateOrders = warningData.some(item => item.deliveryDate === deliveryDate);
// 添加交付日期标题行
const headerRow = document.createElement('tr');
headerRow.style.backgroundColor = '#f0f0f0';
headerRow.innerHTML = `
<td colspan="5">
<strong>交付日期: ${deliveryDate}</strong>
${hasLateOrders ? '<span> (有超期订单)</span>' : '<span> (全部按时)</span>'}
</td>
`;
tbody.appendChild(headerRow);
// 添加该交付日期的所有订单
for (const order of orders) {
// 获取该订单的所有排产记录
const orderSchedule = scheduleData.filter(item =>
item.productId === order.productId &&
item.deliveryDate === order.deliveryDate
);
const isLate = orderSchedule.some(item => item.isLate);
orderSchedule.forEach((item, index) => {
const row = document.createElement('tr');
if (isLate) {
row.classList.add('warning');
}
row.innerHTML = `
<td>${index === 0 ? item.productId : ''}</td>
<td>${item.quantity}</td>
<td>${index === 0 ? item.deliveryDate : ''}</td>
<td>${item.productionDate}</td>
<td>${index === 0 ? (isLate ? '是' : '否') : ''}</td>
`;
tbody.appendChild(row);
});
}
}
}
// 显示预警清单
function displayWarnings() {
const tbody = document.querySelector('#warningTable tbody');
tbody.innerHTML = '';
// 按交付日期分组显示预警
const warningGroups = warningData.reduce((groups, item) => {
if (!groups[item.deliveryDate]) {
groups[item.deliveryDate] = [];
}
groups[item.deliveryDate].push(item);
return groups;
}, {});
const sortedDeliveryDates = Object.keys(warningGroups).sort((a, b) =>
new Date(a) - new Date(b)
);
for (const deliveryDate of sortedDeliveryDates) {
const groupItems = warningGroups[deliveryDate];
// 添加分组标题行
const headerRow = document.createElement('tr');
headerRow.style.backgroundColor = '#f0f0f0';
headerRow.innerHTML = `
<td colspan="5">
<strong>超期交付日期: ${deliveryDate}</strong>
<span> (超期 ${groupItems[0].lateDays} 天)</span>
</td>
`;
tbody.appendChild(headerRow);
// 添加订单行
for (const item of groupItems) {
const row = document.createElement('tr');
row.classList.add('warning');
row.innerHTML = `
<td>${item.productId}</td>
<td>${item.quantity}</td>
<td>${item.deliveryDate}</td>
<td>${item.productionDate}</td>
<td>${item.lateDays}</td>
`;
tbody.appendChild(row);
}
}
}
</script>
</body>
</html>
版权属于:
wehg489
作品采用:
《
署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0)
》许可协议授权
评论