<body>
<div class="container">
<h1>挤出排产</h1>
<div class="input-group">
<input type="file" id="excelFile" accept=".xlsx">
<input type="date" id="startDate" required>
<button onclick="startScheduling()">开始排产</button>
</div>
<div id="scheduleResult"></div>
</div>
<script>
const PRODUCTION_PARAMS = {
speed: {
low: 5000, // ≤16mm
medium: 4000, // 17-30mm
high: 3500 // >30mm
},
diameterGroups: [
{ max: 8, name: '0-8mm' },
{ max: 11, name: '9-11mm' },
{ max: Infinity, name: '12mm+' }
]
};
document.addEventListener('DOMContentLoaded', () => {
if(typeof XLSX === 'undefined') {
alert('错误:核心库加载失败,请检查网络连接');
return;
}
document.getElementById('startDate').valueAsDate = new Date();
});
function startScheduling() {
const fileInput = document.getElementById('excelFile');
const startDate = document.getElementById('startDate').value;
if(!fileInput.files.length) return alert("请选择Excel文件");
if(!startDate) return alert("请选择排产开始日期");
const reader = new FileReader();
reader.onload = processExcelFile;
reader.readAsArrayBuffer(fileInput.files[0]);
}
function processExcelFile(event) {
try {
const workbook = XLSX.read(event.target.result, {type: 'array'});
const sheet = workbook.Sheets[workbook.SheetNames[0]];
const rawData = XLSX.utils.sheet_to_json(sheet, {header:1});
const orders = validateAndFormatData(rawData);
const schedule = generateProductionSchedule(orders);
renderScheduleTable(schedule);
} catch (error) {
handleError('文件处理失败', error);
}
}
function validateAndFormatData(rawData) {
return rawData.slice(1).map((row, index) => {
const dateIndex = 2;
if(row.length < 7) throw new Error(`第${index+2}行数据列数不足`);
let demandDate;
const excelDateValue = row[dateIndex];
if(typeof excelDateValue === 'number') {
const parsed = XLSX.SSF.parse_date_code(excelDateValue);
demandDate = new Date(parsed.y, parsed.m - 1, parsed.d);
} else {
demandDate = new Date(excelDateValue);
}
if(isNaN(demandDate.getTime())) {
throw new Error(`第${index+2}行需求日期格式错误,值:${excelDateValue}`);
}
return {
订单号: String(row[0]).trim(),
需求数量: Number(row[1]),
需求日期: demandDate,
产品口径: Number(row[3]),
实际长度: parseFloat(Number(row[4]).toFixed(2)),
胶料类型: String(row[5]).trim(),
生产机台: String(row[6]).trim(),
挤出基数: Math.ceil(row[1] * 1.1 / 10) * 10,
剩余数量: Math.ceil(row[1] * 1.1 / 10) * 10
};
}).filter(order => order.需求数量 >= 10);
}
function generateProductionSchedule(orders) {
const schedule = {};
const startDate = new Date(document.getElementById('startDate').value);
const grouped = groupOrders(orders);
const sortedKeys = Object.keys(grouped).sort();
sortedKeys.forEach(key => {
const [machine, material, diameterGroup] = key.split('|');
const orderList = grouped[key];
let currentDate = new Date(startDate);
const { maxQty: maxDailyQty } = calculateDailyCapacity(orderList[0].产品口径, orderList[0].实际长度);
while (orderList.some(o => o.剩余数量 > 0)) {
const dateKey = formatDate(currentDate);
if (!schedule[machine]) schedule[machine] = {};
if (!schedule[machine][dateKey]) {
schedule[machine][dateKey] = {
items: [],
totalLength: 0,
usedQty: 0,
capacity: maxDailyQty
};
}
const day = schedule[machine][dateKey];
let capacityLeft = day.capacity - day.usedQty;
for (let order of orderList) {
if (order.剩余数量 <= 0 || capacityLeft <= 0) continue;
const canAlloc = Math.min(order.剩余数量, capacityLeft);
day.items.push({
订单号: order.订单号,
胶料类型: order.胶料类型,
产品口径: order.产品口径,
实际长度: order.实际长度,
排产数量: canAlloc,
排产长度: canAlloc * order.实际长度,
需求日期: order.需求日期
});
day.totalLength += canAlloc * order.实际长度;
day.usedQty += canAlloc;
order.剩余数量 -= canAlloc;
capacityLeft -= canAlloc;
}
currentDate = addDays(currentDate, 1);
}
});
return schedule;
}
function groupOrders(orders) {
const groups = {};
orders.forEach(order => {
const diameterGroup = PRODUCTION_PARAMS.diameterGroups.find(g => order.产品口径 <= g.max).name;
const key = `${order.生产机台}|${order.胶料类型}|${diameterGroup}`;
if (!groups[key]) groups[key] = [];
groups[key].push(order);
});
return groups;
}
function calculateDailyCapacity(diameter, length) {
let speed;
if(diameter <= 16) speed = PRODUCTION_PARAMS.speed.low;
else if(diameter <= 30) speed = PRODUCTION_PARAMS.speed.medium;
else speed = PRODUCTION_PARAMS.speed.high;
const maxQty = Math.floor(speed / length);
return { value: speed, maxQty };
}
function formatDate(date) {
return date.toISOString().split('T')[0];
}
function addDays(date, days) {
const result = new Date(date);
result.setDate(result.getDate() + days);
return result;
}
function renderScheduleTable(schedule) {
let html = '';
Object.entries(schedule).forEach(([machine, dateMap]) => {
html += `<div class="machine-header">
<h3>${machine} 生产计划</h3>
<div class="capacity-info">每日最大产能:${Object.values(dateMap)[0].capacity} 米</div>
</div>`;
html += `<table>
<tr>
<th>生产日期</th>
<th>订单号</th>
<th>口径(mm)</th>
<th>长度(m)</th>
<th>胶料类型</th>
<th>排产数量</th>
<th>总长度(m)</th>
<th>需求日期</th>
<th>状态</th>
</tr>`;
Object.entries(dateMap).forEach(([date, data]) => {
data.items.forEach(item => {
const isLate = new Date(date) > new Date(item.需求日期);
html += `<tr${isLate ? ' class="warning"' : ''}>
<td>${date}</td>
<td>${item.订单号}</td>
<td>${item.产品口径}</td>
<td>${item.实际长度.toFixed(2)}</td>
<td>${item.胶料类型}</td>
<td>${item.排产数量}</td>
<td>${item.排产长度.toFixed(1)}</td>
<td>${item.需求日期.toISOString().split('T')[0]}</td>
<td>${isLate ? '延迟' : '正常'}</td>
</tr>`;
});
html += `<tr class="summary-row">
<td colspan="5">当日汇总</td>
<td>${data.usedQty}</td>
<td>${data.totalLength.toFixed(1)}</td>
<td colspan="3"> </td>
</tr>`;
});
html += `</table>`;
});
document.getElementById('scheduleResult').innerHTML = html;
}
function handleError(message, error) {
console.error(`${message}:`, error);
alert(`${message},请检查控制台获取详细信息`);
}
</script>
</body>
版权属于:
wehg489
作品采用:
《
署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0)
》许可协议授权
评论 (0)