标签搜索

分班逻辑梳理

wehg489
2025-04-06 / 0 评论 / 3 阅读 / 正在检测是否收录...
<script>
function processFile() {
    const fileInput = document.getElementById("fileInput");
    if (!fileInput.files.length) {
        alert("请上传 Excel 文件!");
        return;
    }

    const reader = new FileReader();
    reader.onload = function (e) {
        const data = new Uint8Array(e.target.result);
        const workbook = XLSX.read(data, { type: "array" });

        const sheetName = workbook.SheetNames[0]; 
        const sheet = workbook.Sheets[sheetName];
        const jsonData = XLSX.utils.sheet_to_json(sheet, { header: 1 });

        if (jsonData.length < 2) {
            alert("Excel 文件数据格式不正确!");
            return;
        }

       const formattedData = jsonData.slice(1).map(row => [
    row[0], 
    Number(row[1]), 
    formatExcelDate(row[2]) // 转换Excel日期
]);
        const result = splitProduction(formattedData);
        displayResult(result);
    };
    reader.readAsArrayBuffer(fileInput.files[0]);
}

function splitProduction(data) {
    const groupedByDate = {};
    data.forEach(row => {
        const [productId, quantity, date] = row;
        if (!groupedByDate[date]) groupedByDate[date] = [];
        groupedByDate[date].push({ productId, quantity });
    });

    const result = {};
    Object.keys(groupedByDate).forEach(date => {
        const products = groupedByDate[date].sort((a, b) => b.quantity - a.quantity);
        let A = [], B = [];
        let sumA = 0, sumB = 0;

        products.forEach(item => {
            if (sumA <= sumB) {
                A.push(item);
                sumA += item.quantity;
            } else {
                B.push(item);
                sumB += item.quantity;
            }
        });

        result[date] = { A, B, sumA, sumB, typesA: new Set(A.map(p => p.productId)).size, typesB: new Set(B.map(p => p.productId)).size };
    });

    return result;
}

function displayResult(data) {
    let output = document.getElementById("output");
    output.innerHTML = "";

    Object.keys(data).forEach(date => {
        let summary = `<h3>排产日期:${date}</h3>
        <p class="summary">A 班:总数量 ${data[date].sumA},种类数 ${data[date].typesA} | B 班:总数量 ${data[date].sumB},种类数 ${data[date].typesB}</p>`;

        let table = `<table>
            <tr>
                <th>A 班 - 产品编号</th>
                <th>A 班 - 数量</th>
                <th>B 班 - 产品编号</th>
                <th>B 班 - 数量</th>
            </tr>`;

        const maxLength = Math.max(data[date].A.length, data[date].B.length);
        for (let i = 0; i < maxLength; i++) {
            const Aitem = data[date].A[i] || { productId: "", quantity: "" };
            const Bitem = data[date].B[i] || { productId: "", quantity: "" };
            table += `<tr>
                <td>${Aitem.productId}</td>
                <td>${Aitem.quantity}</td>
                <td>${Bitem.productId}</td>
                <td>${Bitem.quantity}</td>
            </tr>`;
        }
        table += `</table>`;
        output.innerHTML += summary + table;
    });
}
function formatExcelDate(excelSerial) {
    if (typeof excelSerial === "number") {
        const date = new Date((excelSerial - 25569) * 86400 * 1000); 
        return date.toISOString().split("T")[0]; // 转换为 YYYY-MM-DD 格式
    }
    return excelSerial; // 非数字,可能已经是日期字符串
}

</script>
  1. 主要功能
    这是一个生产排班系统,能够:

读取Excel文件中的生产数据
按日期对产品进行分组
将每个日期的生产任务智能分配到A班和B班
尽量平衡两班的工作量(按产品数量)
显示分配结果

  1. 核心函数解析
    processFile() - 文件处理入口
    获取用户上传的Excel文件
    使用FileReader读取文件内容
    调用XLSX库解析Excel数据
    转换数据格式并调用分配逻辑
    splitProduction(data) - 核心分配逻辑
    按日期分组:groupedByDate
    对每个日期的产品按数量降序排序
    使用贪心算法分配:
    始终将当前产品分配给总数量较少的班次
    保持两班总数量尽可能平衡
    displayResult(data) - 结果展示
    为每个日期创建表格显示:
    A班和B班各自的产品列表
    两班的总数量对比
    产品种类数统计
    formatExcelDate() - 日期格式转换
    将Excel的序列日期值(如44197)转换为"YYYY-MM-DD"格式
    Excel日期是从1900年1月1日开始的天数
  2. 数据结构
    输入数据格式:[[产品ID, 数量, 日期], ...]
    分组后结构:{ 日期: [{productId, quantity}, ...] }
    输出结果结构:{ 日期: { A:[], B:[], sumA, sumB, typesA, typesB } }
  3. 算法特点
    使用贪心算法实现近似最优分配
    先排序再分配,确保大任务优先处理
    平衡两班总工作量,同时统计产品种类
  4. 使用说明
    用户上传包含生产数据的Excel文件
    系统自动处理并显示分配结果
    结果展示包括:
    按日期分组的排班表
    两班工作量对比
    详细的产品分配情况
    这个系统适合生产制造企业用于日常生产任务分配,能有效平衡各班次的工作负荷,提高排班效率。
    核心分配逻辑

    
    function splitProduction(data) {
    定义名为 splitProduction 的函数,接收 data 参数(二维数组,格式为 [[产品ID, 数量, 日期], ...])
    
    const groupedByDate = {};
    创建空对象 groupedByDate,用于按日期分组存储数据
    javascript
    Copy Code
    data.forEach(row => {
    遍历 data 数组中的每个元素 row
    
    const [productId, quantity, date] = row;
    解构数组元素,将每行数据分解为 productId(产品ID)、quantity(数量)、date(日期)
    
    if (!groupedByDate[date]) groupedByDate[date] = [];
    如果当前日期不存在于 groupedByDate 中,则创建空数组
    
    groupedByDate[date].push({ productId, quantity });
    将当前行的产品ID和数量以对象形式存入对应日期的数组
    
    });
    结束 data.forEach
    
    const result = {};
    创建空对象 result,用于存储最终结果
    
    Object.keys(groupedByDate).forEach(date => {
    遍历 groupedByDate 的所有日期键
    
    const products = groupedByDate[date].sort((a, b) => b.quantity - a.quantity);
    将当前日期的产品按数量降序排序(从大到小)
    
    let A = [], B = [];
    初始化分组数组 A 和 B
    
    let sumA = 0, sumB = 0;
    初始化分组总数量统计变量
    
    products.forEach(item => {
    遍历排序后的产品数组
    
    if (sumA <= sumB) {
    比较两组当前总数量,如果 A 组总和不大于 B 组
    
    A.push(item);
    sumA += item.quantity;
    将当前产品加入 A 组,并累加数量到 sumA
    
    } else {
    B.push(item);
    sumB += item.quantity;
    否则加入 B 组,并累加数量到 sumB
    
    }
    });
    结束产品遍历
    
    result[date] = { 
      A, 
      B,
      sumA,
      sumB,
      typesA: new Set(A.map(p => p.productId)).size,
      typesB: new Set(B.map(p => p.productId)).size 
    };
    将结果存入 result 对象,包含:
    分组数组 A/B
    分组总数量 sumA/sumB
    使用 Set 计算每组中不同产品ID的数量(去重后的种类数)
    
    });
    结束日期遍历
    
    return result;
    }
    返回最终结果对象

    主要功能‌:将每日的生产数据按数量降序排列后,通过贪心算法将产品分配到 A/B 两个组,尽可能平衡两组的总数量,同时统计每组的产品种类数量。返回按日期分组的结果。

‌算法特点‌:优先分配大数量产品到当前总和较小的组,可有效减少分组后的数量差异,但不一定是最优解。时间复杂度为 O(n log n)(主要来自排序操作)。

0

评论 (0)

取消
歌曲封面
0:00