首页
关于
友链
推荐
肥啾解析
百度一下
肥啾GPT
Search
1
宝塔面板登录 phpMyAdmin 提示服务器和客户端上指示的HTTPS之间不匹配
269 阅读
2
Customer complaints evolve with in-car tech
188 阅读
3
JavaScript解析
153 阅读
4
内连接,左连接,右连接作用及区别
111 阅读
5
所谓关系
109 阅读
默认分类
网游架设
手机游戏
python
PHP
Mysql
VBA
C++
JAVASCRIPT
javascript基础
Oracle
生产管理
计划控制
ERP系统开发
APS排产
MES研究
考勤系统
CPA
财管
实务
经济法
战略
审计
税法
藏书架
古典名著
世界名著
编程秘籍
攻防渗透
经管书籍
大佬传经
风雅读物
考试相关
心情格言
拾玉良言
外文报刊
外刊随选
Facebook
Twitter
China Daily
软考
登录
Search
标签搜索
期刊读物
古文
何瑜明
累计撰写
160
篇文章
累计收到
154
条评论
首页
栏目
默认分类
网游架设
手机游戏
python
PHP
Mysql
VBA
C++
JAVASCRIPT
javascript基础
Oracle
生产管理
计划控制
ERP系统开发
APS排产
MES研究
考勤系统
CPA
财管
实务
经济法
战略
审计
税法
藏书架
古典名著
世界名著
编程秘籍
攻防渗透
经管书籍
大佬传经
风雅读物
考试相关
心情格言
拾玉良言
外文报刊
外刊随选
Facebook
Twitter
China Daily
软考
页面
关于
友链
推荐
肥啾解析
百度一下
肥啾GPT
搜索到
9
篇与
的结果
2025-07-15
测试
<?php header('Content-Type: application/json'); header('Access-Control-Allow-Origin: *'); header('Access-Control-Allow-Methods: POST'); // 配置常量 - 同步前端最新配置 define('CONFIG_VALUES', [ '动力类型' => ['FD', 'FB', 'FLB', 'FTB', 'FG', 'FCG', 'FRB', 'FR4D'], '吨位' => [10, 15, 18, 19, 20, 25, 30, 35, 40, 45, '50F', '50TF', 50, 70, 80, 100, '1A', '1B', '1C'], '发动机型号' => ['4JG2', '4TNE98', '6BG1', 'C240', 'K21', 'K21双燃料', 'K25', 'K25双燃料', 'PSI2.4', 'S4S', 'S6S', 'S6S-T', 'L4C25-T', 'V2403-M', 'V2403-CR', 'V2403', 'WG2503-LPG', '全柴485', 'V29-V037', '3E22YG51', '新柴485', '4D27G31', '4D29X41-024', '4D32G31', 'A495BPG', 'A498BPG', 'A498BT1', '新柴C490(国2)', '新柴C490(欧3A)', 'YC4A115-T300'], '国产发动机' => ['A495BPG', 'A498BPG', 'A498BT1', '新柴C490(国2)', '全柴485', '4D29X41-024', '3E22YG51'], '有加速发动机' => ['4TNE98', '6BG1', 'S6S-T', 'S6S', '4JG2', 'C240', 'K25', 'K21', 'C490BPG', 'A498BPG', '4D27G31', 'A495BPG', 'A498BT1', 'C490BPG-51', 'NC485', '4B4-45V32', 'V2403-M', 'S4S'], '电控品牌' => ['Inmotion', '凡几', '嘉晨', '松正', '柯蒂斯', '麦格米特', '维德G2', '维德G3', 'S2', 'M1', '维德25G2', '维德35G3', '维德50G1'], '国产电控' => ['凡几', '嘉晨'], '电压等级' => ['48V铅酸', '51.2V锂电', '72V铅酸', '76.8V锂电', '80V铅酸', '205Ah', '230Ah', '173Ah', '280Ah', '410Ah'], '挡板' => ['铅酸', '锂电'], '年款' => ['16', '17'], '阀片' => ['2SV', '3SV', '4SV', '5SV'], '座椅' => ['丰田样式悬浮座椅', 'TCM样式座椅', 'TCM样式悬浮座椅', '格拉默座椅', '丰田样式座椅(STD)', '丰田样式座椅+座椅开关', '格拉默座椅+拇指开关', 'TCM样式座椅+座椅开关', '薄型座椅', 'TCM样式悬浮座椅+座椅开关', '丰田样式悬浮座椅+座椅开关', '丰田样式悬浮座椅+座椅开关+扶手', '合力样式悬浮座椅'], '默认座椅' => ['丰田样式悬浮座椅'], '其它' => ['闪光警示灯', '蓝光灯', '工作灯', '大灯防护罩', '后腿架', '半圆后视镜', '加长把手', '加长前腿把手', '线槽盖板', '前雨挡', '上雨挡', '后雨挡', '一字红光灯', '货叉架摄像头', '铸造后桥', '加大水箱', '加高水箱', '油水分离', '高排消声器', '灭火消声器', '左高排气', '右高排气', 'OPS', 'LPG', '高挂预滤器', '实心胎', '环保实心胎', '隔音', '手刹开关', '手刹报警', '油水分离', '十叶风扇', '座椅开关', '索达多路阀', '格拉默座椅', '刷卡仪表', '分体式', '前双胎', 'BATI', 'FLB方向盘', '电瓶挡板', 'EGLM', '进箱', '半交流', '暖风', '驾驶室', '充气(STD)', '软连接', '后腿把手', '2SV', '3SV', '4SV', '5SV', '下进气', '高排气', '实心', '加大电瓶'], '其它2' => ['闪光警示灯', '蓝光灯', '工作灯', '大灯防护罩', '后腿架', '半圆后视镜', '加长把手', '加长前腿把手', '线槽盖板', '前雨挡', '上雨挡', '后雨挡', '一字红光灯', '货叉架摄像头', '铸造后桥', '加大水箱', '加高水箱', '油水分离', '高排消声器', '灭火消声器', '左高排气', '右高排气', 'OPS', 'LPG', '高挂预滤器', '实心胎', '环保实心胎', '隔音', '手刹开关', '手刹报警', '油水分离', '十叶风扇', '座椅开关', '索达多路阀', '格拉默座椅', '刷卡仪表', '分体式', '前双胎', 'BATI', 'FLB方向盘', '电瓶挡板', 'EGLM', '进箱', '半交流', '暖风', '驾驶室', '空调', '充气(STD)', '软连接', '后腿把手', '2SV', '3SV', '4SV', '5SV', '下进气', '高排气', '扶手', '电液比例', '实心', '加大电瓶'], '默认轮胎' => ['国产品牌', '充气(STD)'] ]); define('DEFAULT_CONFIGS', [ '充气(STD)', '国产品牌', '丰田样式悬浮座椅' ]); // 发动机型号代称映射表(同步前端) define('ENGINE_ALIASES', [ 'NC485' => '新柴485(国2)', 'C490BPG' => '新柴490(国2)', 'A495BPG' => '新柴495(国2)', 'A498BPG' => '新柴498(国2)', '4N23G31' => '新柴485(国3)', '4D27G31' => '新柴490(国3)', '4D30G31' => '新柴495(国3)', '4D32G31' => '新柴498(国3)', '4D35ZG' => '新柴4D35(国3)', 'C490BPG-51' => '新柴490(欧3A)', 'A498BT1' => '新柴498(欧3A)', '4D29X41-024' => '新柴490(国4)', '3E22YG51' => '新柴(欧5)', '4B4-45V32' => '全柴485(国3)', 'GQ4Y' => 'GQ4Y', '6102' => '6102(国3)', 'YC4A115-T300' => '玉柴(国3)', 'C240' => 'C240(ⅢA/国3)', '4JG2' => '4JG2(欧3A)', '6BG1' => '6BG1', 'K21' => 'K21', 'K25' => 'K25', 'K25EPA' => 'K25EPA', 'S4S' => 'S4S(国3)', '4EG' => '4EG', 'S6S' => 'S6S(ⅢA)', 'S6S-T' => 'S6S-T(国三)', '4TNE98' => '4TNE98(国3)', '4TNV94L' => '4TNV94L(国4)', 'V2403' => 'V2403-CR', 'WG2503-L' => 'WG2503-L', 'QSF3.8' => 'QSF3.8(国3)', 'L4CRTV' => '韩国LS(欧5)', 'PSI认证版' => 'PSI2.4', 'WG2503-GL' => 'WG2503-GL', 'V2403-M' => 'V2403-M', 'V29-50V42' => '全柴490(国4)', '6102(国3)' => '6102', '全柴485(国3)' => '4B4-45V32', '新柴490(国3)' => '4D27G31', '新柴485(国3)' => '4N23G31', '新柴495(国3)' => '4D30G31', '新柴498(国3)' => '4D32G31', '新柴4D35(国3)' => '4D35ZG', '新柴490(国4)' => '4D29X41-024', '4TNV94L(国4)' => '4TNV94L', '全柴490(国4)' => 'V29-50V42', '新柴495(国2)' => 'A495BPG', '新柴498(国2)' => 'A498BPG', '新柴490(国2)' => 'C490BPG', '新柴485(国2)' => 'NC485', '新柴490(欧3A)' => 'C490BPG-51', '新柴498(欧3A)' => 'A498BT1', '新柴(欧5)' => '3E22YG51', 'C240(ⅢA/国3)' => 'C240', '4JG2(欧3A)' => '4JG2', 'GQ4Y' => 'GQ4Y', '玉柴(国3)' => 'YC4A115-T300', '6BG1' => '6BG1', 'K21' => 'K21', 'K25' => 'K25', 'K25EPA' => 'K25EPA', 'S4S(国3)' => 'S4S', '4EG' => '4EG', 'S6S(ⅢA)' => 'S6S', 'S6S-T(国三)' => 'S6S-T', '4TNE98(国3)' => '4TNE98', 'V2403-CR' => 'V2403', 'WG2503-L' => 'WG2503-L', 'QSF3.8(国3)' => 'QSF3.8', '韩国LS(欧5)' => 'L4CRTV', 'PSI2.4' => 'PSI认证版', 'WG2503-GL' => 'WG2503-GL', 'V2403-M' => 'V2403-M', '35G3' => '维德G3', '/G3' => '维德G3' ]); // 获取输入数据 $input = json_decode(file_get_contents('php://input'), true); $productDesc = isset($input['productDesc']) ? trim($input['productDesc']) : ''; $spec = isset($input['spec']) ? trim($input['spec']) : ''; $debugSystem = isset($input['debugSystem']) ? trim($input['debugSystem']) : ''; // 新增调试系统参数 if ($productDesc === '' || $spec === '') { exitJson(['error' => '缺少品号描述或规格']); } // 读取配置数据 $configData = json_decode(file_get_contents('data.json'), true); if (!$configData) { exitJson(['error' => '配置数据未加载']); } // 预处理配置数据:替换发动机代称为标准名称(同步前端逻辑) $configData = array_map(function($item) { $newItem = $item; // 处理规格字段 foreach (ENGINE_ALIASES as $alias => $standard) { if (strpos($newItem['规格'], $alias) !== false) { $newItem['规格'] = str_replace($alias, $standard, $newItem['规格']); } } // 处理名称字段 foreach (ENGINE_ALIASES as $alias => $standard) { if (strpos($newItem['名称'], $alias) !== false) { $newItem['名称'] = str_replace($alias, $standard, $newItem['名称']); } } return $newItem; }, $configData); // 提取动力类型 $powerType = extractPowerType($productDesc, $spec); if (!$powerType) { exitJson(['error' => '无法识别动力类型']); } // 读取系统配置文件 $sysFile = 'systems_' . $powerType . '.json'; if (!file_exists($sysFile)) { exitJson(['error' => '缺少 ' . $powerType . ' 的系统配置文件']); } $systemConfig = json_decode(file_get_contents($sysFile), true); // 提取关键配置值(同步前端逻辑,新增发动机代称处理) $extractedValues = extractConfigValues($productDesc, $spec, $powerType); // 执行匹配(新增调试系统参数) $results = matchConfigurations($configData, $productDesc, $spec, $systemConfig, $extractedValues, $debugSystem); exitJson([ 'powerType' => $powerType, 'extractedValues' => $extractedValues, 'matched' => $results['matched'], 'debugInfo' => $results['debugInfo'] // 返回调试信息 ]); // ========== 函数区 ========== function exitJson($data) { echo json_encode($data, JSON_UNESCAPED_UNICODE); exit; } // 提取动力类型(逻辑不变) function extractPowerType($productDesc, $spec) { $text = $productDesc . ' ' . $spec; foreach (CONFIG_VALUES['动力类型'] as $type) { if (stripos($text, $type) !== false) { return $type; } } return null; } // 提取配置值(同步前端,新增发动机代称处理) function extractConfigValues($productDesc, $spec, $powerType) { $values = [ '动力类型' => $powerType, '吨位' => null, '发动机型号' => null, '电控品牌' => null, '电压等级' => null, '挡板' => null, '年款' => null, '阀片' => null, '座椅' => null ]; $allText = $productDesc . ' ' . $spec; $prodesc = $productDesc; // 提取吨位 - 支持50F、50TF格式 if (preg_match('/(\d+[FT]?F|\d+)/', $prodesc, $tonMatch)) { $matchedTon = $tonMatch[0]; // 处理数字吨位 if (is_numeric($matchedTon)) { $matchedTon = intval($matchedTon); } if (in_array($matchedTon, CONFIG_VALUES['吨位'])) { $values['吨位'] = $matchedTon; } } // 根据动力类型提取不同字段(新增发动机代称映射) if (in_array($powerType, ['FD', 'FR4D', 'FCG', 'FG'])) { // 柴油车型:先匹配标准型号,再匹配代称 $foundEngine = null; // 1. 匹配标准发动机型号 foreach (CONFIG_VALUES['发动机型号'] as $engine) { if (strpos($allText, $engine) !== false) { $foundEngine = $engine; break; } } // 2. 未找到标准型号,匹配代称 if (!$foundEngine) { foreach (ENGINE_ALIASES as $alias => $standard) { if (strpos($allText, $alias) !== false) { $foundEngine = $standard; break; } } } $values['发动机型号'] = $foundEngine; } else if (in_array($powerType, ['FB', 'FLB', 'FRB', 'FTB'])) { // 电动车型 foreach (CONFIG_VALUES['电控品牌'] as $ec) { if (strpos($allText, $ec) !== false) { $values['电控品牌'] = $ec; break; } } foreach (CONFIG_VALUES['电压等级'] as $level) { if (strpos($allText, $level) !== false) { $values['电压等级'] = $level; break; } } foreach (CONFIG_VALUES['挡板'] as $board) { if (strpos($allText, $board) !== false) { $values['挡板'] = $board; break; } } } // 提取其他通用字段 foreach (CONFIG_VALUES['年款'] as $year) { if (strpos($allText, $year) !== false) { $values['年款'] = $year; break; } } foreach (CONFIG_VALUES['阀片'] as $valve) { if (strpos($allText, $valve) !== false) { $values['阀片'] = $valve; break; } } foreach (CONFIG_VALUES['座椅'] as $seat) { if (strpos($allText, $seat) !== false) { $values['座椅'] = $seat; break; } } return $values; } // 匹配配置(新增调试信息返回,同步前端特殊规则) function matchConfigurations($data, $productDesc, $spec, $systemConfig, $extractedValues, $debugSystem) { $allText = $productDesc . ' ' . $spec; $allTextLower = mb_strtolower($allText, 'UTF-8'); $results = []; $debugInfo = []; // 存储调试信息 $defaultConfigsLower = array_map('mb_strtolower', DEFAULT_CONFIGS); $otherKeywords = array_map('mb_strtolower', CONFIG_VALUES['其它']); $otherKeywords2 = array_map('mb_strtolower', CONFIG_VALUES['其它2']); // 确定国产标志(同步前端逻辑) $isDomestic = false; $powerType = $extractedValues['动力类型']; if (in_array($powerType, ['FD', 'FR4D', 'FCG', 'FG'])) { $engineType = $extractedValues['发动机型号'] ?? null; $isDomestic = $engineType && in_array($engineType, CONFIG_VALUES['国产发动机']); } else if (in_array($powerType, ['FB', 'FLB', 'FRB', 'FTB'])) { $electricControl = $extractedValues['电控品牌'] ?? null; $isDomestic = $electricControl && in_array($electricControl, CONFIG_VALUES['国产电控']); } foreach ($systemConfig['order'] as $system) { // 筛选当前系统的配置项 $items = array_filter($data, function($item) use ($system) { return strpos($item['分类'], $system) !== false; }); $items = array_values($items); $keywordRule = $systemConfig['keywords'][$system] ?? ['required' => [], 'optional' => []]; $bestItem = null; $bestScore = PHP_INT_MIN; $candidateDebug = []; // 单个系统的候选调试信息 foreach ($items as $item) { // 计算匹配分数(同步前端逻辑,新增特殊规则) $scoreData = calculateMatchScore($item, $system, $keywordRule, $extractedValues, $allText, $isDomestic); $score = $scoreData['score']; $details = $scoreData['details']; // 存储候选调试信息 $candidateDebug[] = [ 'item' => [ '编号' => $item['编号'], '名称' => $item['名称'], '规格' => $item['规格'] ], 'score' => $score, 'details' => $details ]; if ($score === -9999) { continue; } // 检查非默认配置项是否在输入中存在(同步前端) $itemSpecLower = mb_strtolower($item['规格'], 'UTF-8'); $nonDefaultKeywords = array_diff($otherKeywords, $defaultConfigsLower); $hasUnmatchedNonDefault = false; foreach ($nonDefaultKeywords as $keyword) { if (strpos($itemSpecLower, $keyword) !== false && strpos($allTextLower, $keyword) === false) { $hasUnmatchedNonDefault = true; break; } } if ($hasUnmatchedNonDefault) { $score -= 50; $details['optional'][] = '存在非默认配置项未在输入中匹配,扣50分'; } // 更新最佳匹配 if ($score > $bestScore) { $bestScore = $score; $bestItem = $item; $bestDetails = $details; } } // 检查特殊规则(同步前端所有规则) $specialRule = checkSpecialRules($system, $bestItem, $extractedValues, $allText); if ($bestItem && !$specialRule['pass']) { $bestItem = null; $bestScore = 0; $bestDetails['specialNote'] = $specialRule['note']; } else if ($bestItem) { $bestDetails['specialNote'] = $specialRule['note']; } // 存储当前系统结果 $results[] = $bestItem ? [ 'system' => $system, '编号' => $bestItem['编号'], '规格' => $bestItem['规格'], '名称' => $bestItem['名称'], 'score' => $bestScore, 'details' => $bestDetails ] : [ 'system' => $system, '编号' => null, '规格' => null, '名称' => null, 'score' => 0, 'details' => ['specialNote' => $specialRule['note'] ?? '未找到匹配配置'] ]; // 存储调试信息(仅当指定调试系统时) if ($debugSystem === $system) { $debugInfo = [ 'system' => $system, 'extractedValues' => $extractedValues, 'candidates' => $candidateDebug, 'bestCandidate' => $bestItem ? [ 'item' => $bestItem, 'score' => $bestScore, 'details' => $bestDetails ] : null ]; } } return [ 'matched' => $results, 'debugInfo' => $debugInfo ]; } // 计算匹配分数(同步前端逻辑,新增特殊规则加分) function calculateMatchScore($item, $system, $keywordRule, $extractedValues, $allText, $isDomestic) { $score = 0; $allTextLower = mb_strtolower($allText, 'UTF-8'); $itemSpecLower = mb_strtolower($item['规格'], 'UTF-8'); $itemNameLower = mb_strtolower($item['名称'], 'UTF-8'); $details = [ 'required' => [], 'optional' => [], 'tonnageMatch' => false, 'score' => 0 ]; $powerType = $extractedValues['动力类型']; $deus = explode(',', $item['分类']); // 拆分分类字段 // 1. 检查必须关键字 foreach ($keywordRule['required'] as $key) { $value = $extractedValues[$key] ?? null; if (!$value) { $details['required'][] = "必须关键字【$key】未提取到值,匹配失败"; return ['score' => -9999, 'details' => $details]; } // 吨位特殊处理(支持范围和单值) if ($key === '吨位') { $tonResult = matchTonnage($item['规格'], $extractedValues); if (!$tonResult['matched']) { $details['required'][] = "吨位【$value】不匹配配置范围,匹配失败"; return ['score' => -9999, 'details' => $details]; } $score += 30; $details['required'][] = "吨位【$value】匹配配置范围【{$tonResult['range']}】,加30分"; $details['tonnageMatch'] = true; continue; } // 发动机型号特殊处理(支持代称) if ($key === '发动机型号') { $allEngineNames = [$value]; // 获取所有相关代称 foreach (ENGINE_ALIASES as $alias => $standard) { if ($standard === $value) $allEngineNames[] = $alias; if ($alias === $value) $allEngineNames[] = $standard; } $isMatched = false; foreach ($allEngineNames as $name) { if (strpos($item['规格'], $name) !== false && strpos($allText, $name) !== false) { $isMatched = true; break; } } if (!$isMatched) { $details['required'][] = "发动机型号【$value】及其代称不匹配,匹配失败"; return ['score' => -9999, 'details' => $details]; } $score += 30; $details['required'][] = "发动机型号【$value】匹配,加30分"; continue; } // 其他必须关键字 if (strpos($item['规格'], $value) === false) { $details['required'][] = "必须关键字【$key:$value】不匹配配置,匹配失败"; return ['score' => -9999, 'details' => $details]; } $score += 30; $details['required'][] = "必须关键字【$key:$value】匹配,加30分"; } // 2. 检查可选关键字 foreach ($keywordRule['optional'] as $key) { $value = $extractedValues[$key] ?? null; if ($value && strpos($item['规格'], $value) !== false) { $score += 20; $details['optional'][] = "可选关键字【$key:$value】匹配,加20分"; } // 年款未区分时加分 if ($key === '年款' && !$value && !strpos($item['规格'], '16') && !strpos($item['规格'], '17')) { $score += 20; $details['optional'][] = "未检测到年款区分,加20分"; } } // 3. 特殊系统规则(同步前端所有特殊规则) $systemLower = mb_strtolower($system, 'UTF-8'); // 多路阀规则 if (in_array('多路阀', $deus) || strpos($systemLower, '多路阀') !== false) { if ($isDomestic) { if (!strpos($allTextLower, '进口品牌多路阀') && !strpos($allTextLower, '国产品牌多路阀') && !strpos($allTextLower, '3e22yg51') && strpos($itemNameLower, '国产品牌') !== false) { $score += 20; $details['optional'][] = '国产发动机且非欧五,多路阀国产品牌匹配,加20分'; } else if (strpos($allTextLower, '进口品牌多路阀') !== false && strpos($itemNameLower, '进口品牌') !== false) { $score += 20; $details['optional'][] = '国产发动机但要求进口多路阀,匹配加20分'; } else if (!strpos($allTextLower, '进口品牌多路阀') && !strpos($allTextLower, '国产品牌多路阀') && strpos($allTextLower, '3e22yg51') !== false && strpos($itemNameLower, '进口品牌') !== false) { $score += 20; $details['optional'][] = '欧五发动机默认进口多路阀,匹配加20分'; } } else { if (!strpos($allTextLower, '国产品牌多路阀') && strpos($itemNameLower, '进口品牌') !== false) { $score += 20; $details['optional'][] = '进口发动机多路阀进口品牌匹配,加20分'; } else if (strpos($allTextLower, '国产品牌多路阀') !== false && strpos($itemNameLower, '国产品牌') !== false) { $score += 20; $details['optional'][] = '进口发动机要求国产多路阀,匹配加20分'; } } } // 液压泵规则 if (in_array('液压泵', $deus) || strpos($systemLower, '液压泵') !== false) { if ($isDomestic) { if (!strpos($allTextLower, '进口品牌液压泵') && strpos($itemNameLower, '国产品牌') !== false) { $score += 20; $details['optional'][] = '国产发动机液压泵国产品牌匹配,加20分'; } else if (strpos($allTextLower, '进口品牌液压泵') !== false && strpos($itemNameLower, '进口品牌') !== false) { $score += 20; $details['optional'][] = '国产发动机要求进口液压泵,匹配加20分'; } } else { if (!strpos($allTextLower, '国产品牌液压泵') && strpos($itemNameLower, '进口品牌') !== false) { $score += 20; $details['optional'][] = '进口发动机液压泵进口品牌匹配,加20分'; } else if (strpos($allTextLower, '国产品牌液压泵') !== false && strpos($itemNameLower, '国产品牌') !== false) { $score += 20; $details['optional'][] = '进口发动机要求国产液压泵,匹配加20分'; } } } // 转向器规则 if (in_array('转向', $deus) || strpos($systemLower, '转向') !== false) { if ($isDomestic) { if (!strpos($allTextLower, '进口品牌转向器') && strpos($itemNameLower, '国产品牌') !== false) { $score += 20; $details['optional'][] = '国产发动机转向器国产品牌匹配,加20分'; } else if (strpos($allTextLower, '进口品牌转向器') !== false && strpos($itemNameLower, '进口品牌') !== false) { $score += 20; $details['optional'][] = '国产发动机要求进口转向器,匹配加20分'; } } else { if (!strpos($allTextLower, '国产品牌转向器') && strpos($itemNameLower, '进口品牌') !== false) { $score += 20; $details['optional'][] = '进口发动机转向器进口品牌匹配,加20分'; } else if (strpos($allTextLower, '国产品牌转向器') !== false && strpos($itemNameLower, '国产品牌') !== false) { $score += 20; $details['optional'][] = '进口发动机要求国产转向器,匹配加20分'; } } } // 轮胎规则 if (in_array('轮胎', $deus) || strpos($systemLower, '胎') !== false) { if (!strpos($allTextLower, '实心胎') && !strpos($allTextLower, '充气胎') && strpos($itemSpecLower, '国产品牌') !== false && strpos($itemSpecLower, 'std') !== false) { if (strpos($allTextLower, '前双胎') !== false && strpos($itemSpecLower, '前双胎') !== false) { $score += 30; $details['optional'][] = '前双胎特征匹配,加30分'; } $score += 100; $details['optional'][] = '默认国产品牌充气胎,加100分'; } else if (strpos($allTextLower, '实心胎(国产品牌)') !== false && strpos($itemSpecLower, '国产品牌') !== false && strpos($itemSpecLower, '实心') !== false) { $score += 30; $details['optional'][] = '国产品牌+实心胎匹配,加30分'; } else if (strpos($allTextLower, '实心胎(进口品牌)') !== false && strpos($itemSpecLower, '进口品牌') !== false && strpos($itemSpecLower, '实心') !== false) { $score += 30; $details['optional'][] = '进口品牌+实心胎匹配,加30分'; } else if (strpos($allTextLower, '充气胎(进口品牌)') !== false && strpos($itemSpecLower, '进口品牌') !== false && strpos($itemSpecLower, '充气') !== false) { $score += 30; $details['optional'][] = '进口品牌+充气胎匹配,加30分'; } if (!strpos($allTextLower, '前双胎') && strpos($itemSpecLower, '前双胎') !== false) { $score -= 30; $details['optional'][] = '配置有前双胎但输入无,扣30分'; } } // 覆盖件规则 if (in_array('覆盖件', $deus) || strpos($systemLower, '覆盖件') !== false) { if (strpos($allTextLower, '隔音') !== false && strpos($itemSpecLower, '隔音') !== false) { $score += 20; $details['optional'][] = '隔音要求匹配,加20分'; } else if (!strpos($allTextLower, '隔音') && !strpos($itemSpecLower, '隔音')) { $score += 20; $details['optional'][] = '无隔音要求匹配,加20分'; } } // 座椅规则 if (in_array('座椅', $deus) || strpos($systemLower, '座椅') !== false) { if (!strpos($allTextLower, '座椅') && !strpos($allTextLower, 'fr4d') && !strpos($allTextLower, 'frb') && !strpos($allTextLower, '软连接') && strpos($itemSpecLower, '丰田样式座椅std') !== false) { $score += 20; $details['optional'][] = '默认丰田样式座椅(STD)匹配,加20分'; } } // 驻车制动规则 if (in_array('驻车制动', $deus) || strpos($systemLower, '驻车制动') !== false) { if (strpos($allTextLower, '凡几') !== false && strpos($itemSpecLower, '手刹开关') !== false) { $score += 60; $details['optional'][] = '凡几电控默认手刹开关,加60分'; } if (strpos($allTextLower, '仪表显示') !== false && strpos($itemSpecLower, '手刹开关') !== false) { $score += 20; $details['optional'][] = '仪表显示P要求手刹开关,加20分'; } if ((strpos($allTextLower, '3e22yg51') !== false || strpos($allTextLower, '维德') !== false || strpos($allTextLower, '2403') !== false || strpos($allTextLower, 'wg2503') !== false || strpos($allTextLower, 'psi') !== false || strpos($allTextLower, 'l4crtv') !== false) && strpos($itemSpecLower, '手刹开关') !== false) { $score += 50; $details['optional'][] = '17款欧五/维德等默认手刹开关,加50分'; } } // 欧五发动机下进气规则 if ((in_array('覆盖件', $deus) || strpos($systemLower, '覆盖件') !== false) && (strpos($allTextLower, '3e22yg51') !== false || strpos($allTextLower, 'wg2503-l') !== false || strpos($allTextLower, 'v2403') !== false || strpos($allTextLower, 'psi') !== false || strpos($allTextLower, 'l4crtv') !== false)) { if (strpos($itemSpecLower, '下进气') !== false) { $score += 50; $details['optional'][] = '欧五/久保田等默认下进气,加50分'; } } // 刷卡仪表规则 if (strpos($allTextLower, '刷卡仪表') !== false && strpos($itemSpecLower, 'tsg81') !== false) { $score += 20; $details['optional'][] = '刷卡仪表要求TSG81,加20分'; } // 凡几/嘉晨侧装规则 if ((strpos($allTextLower, '凡几') !== false && strpos($allTextLower, '51.2v锂电') !== false) || (strpos($allTextLower, '嘉晨') !== false && strpos($allTextLower, '76.8v锂电') !== false)) { if (strpos($itemSpecLower, '侧装') !== false) { $score += 20; $details['optional'][] = '侧装配置匹配,加20分'; } if ((in_array('平衡重', $deus) || strpos($systemLower, '平衡重') !== false) && strpos($itemSpecLower, '实心') !== false) { $score += 50; $details['optional'][] = '侧装平衡重含实心配置,加50分'; } } // 50F/50TF规则 if (strpos($allTextLower, '50f') !== false && strpos($itemSpecLower, '50f') !== false) { $score += 20; $details['optional'][] = '50F配置匹配,加20分'; } if (strpos($allTextLower, '50tf') !== false && strpos($itemSpecLower, '50tf') !== false) { $score += 20; $details['optional'][] = '50TF配置匹配,加20分'; } // 发动机型号区分规则(S6S/S6S-T、V2403系列) if (strpos($allTextLower, 's6s') !== false && !strpos($allTextLower, 's6s-t') && strpos($itemSpecLower, 's6s-t') !== false) { $score = 0; $details['optional'][] = 'S6S发动机排除S6S-T配置,得分归零'; } if (strpos($allTextLower, 'v2403') !== false && !strpos($allTextLower, 'v2403-m') && !strpos($allTextLower, 'v2403-cr') && (strpos($itemSpecLower, 'v2403-m') !== false || strpos($itemSpecLower, 'v2403-cr') !== false)) { $score = 0; $details['optional'][] = 'V2403发动机排除V2403-M/V2403-CR配置,得分归零'; } // 手刹未拉报警规则 if (strpos($allTextLower, '3e22yg51') !== false && strpos($allTextLower, '手刹未拉') !== false && strpos($itemSpecLower, '断电手刹报警') !== false) { $score += 50; $details['optional'][] = '欧五发动机手刹未拉提醒,断电手刹报警匹配,加50分'; } if (strpos($allTextLower, 'inmotion') !== false && strpos($allTextLower, '手刹未拉') !== false && strpos($itemSpecLower, '断电手刹报警') !== false) { $score += 50; $details['optional'][] = 'Inmotion电控手刹未拉提醒,断电手刹报警匹配,加50分'; } // 分体式护顶架规则 if (strpos($allTextLower, '分体式') !== false && strpos($itemNameLower, '分体式') !== false) { $score += 30; $details['optional'][] = '分体式护顶架匹配,加30分'; } // 驾驶室规则 if (strpos($allTextLower, '驾驶室') !== false && !strpos($allTextLower, '空调驾驶室') && !strpos($allTextLower, '暖风') && strpos($itemNameLower, '驾驶室') !== false && !strpos($itemNameLower, '空调') && !strpos($itemNameLower, '暖风')) { $score += 20; $details['optional'][] = '普通驾驶室匹配,加20分'; } if (!strpos($allTextLower, '驾驶室') && !strpos($allTextLower, '空调驾驶室') && !strpos($allTextLower, '暖风') && !strpos($itemNameLower, '驾驶室') && !strpos($itemNameLower, '空调') && !strpos($itemNameLower, '暖风')) { $score += 20; $details['optional'][] = '无驾驶室要求匹配,加20分'; } // 其他配置项匹配(CONFIG_VALUES['其它2']) foreach (CONFIG_VALUES['其它2'] as $keyword) { $keywordLower = mb_strtolower($keyword, 'UTF-8'); if (strpos($itemSpecLower, $keywordLower) !== false) { if (strpos($allTextLower, $keywordLower) !== false) { $score += 1; $details['optional'][] = "配置【$keyword】匹配,加1分"; } else { $score -= 30; $details['optional'][] = "配置【$keyword】存在但输入无,扣30分"; } } } $details['score'] = round($score, 1); return ['score' => $score, 'details' => $details]; } // 吨位匹配(支持范围、单值、50F/50TF格式) function matchTonnage($itemSpec, $extractedValues) { $result = ['matched' => false, 'range' => null]; $powerType = $extractedValues['动力类型'] ?? ''; $userTonnage = $extractedValues['吨位'] ?? null; if (!$powerType || $userTonnage === null) { return $result; } // 处理50F、50TF等格式 if (is_string($userTonnage) && preg_match('/(\d+)/', $userTonnage, $numMatch)) { $userTonnageNum = intval($numMatch[1]); } else { $userTonnageNum = intval($userTonnage); } // 查找动力类型位置 $powerPos = mb_strpos($itemSpec, $powerType); if ($powerPos === false) { return $result; } // 提取吨位部分 $tonnagePart = mb_substr($itemSpec, $powerPos + mb_strlen($powerType)); // 1. 匹配范围格式(如:10~30、20-50) if (preg_match('/(\d+)\s*[~-]\s*(\d+)/', $tonnagePart, $rangeMatches)) { $min = intval($rangeMatches[1]); $max = intval($rangeMatches[2]); if ($userTonnageNum >= $min && $userTonnageNum <= $max) { $result['matched'] = true; $result['range'] = "$min-$max"; } return $result; } // 2. 匹配单值格式(如:FD30T、FB25) if (preg_match('/\b(\d+)\b/', $tonnagePart, $singleMatch)) { $specValue = intval($singleMatch[1]); if ($userTonnageNum === $specValue) { $result['matched'] = true; $result['range'] = (string)$specValue; } return $result; } // 3. 匹配字符串格式(如:50F、50TF) if (preg_match('/(\d+[FT]?F)/', $tonnagePart, $strMatch)) { if ($strMatch[1] === $userTonnage) { $result['matched'] = true; $result['range'] = $strMatch[1]; } return $result; } // 4. 直接匹配 if (strpos($itemSpec, (string)$userTonnage) !== false) { $result['matched'] = true; $result['range'] = (string)$userTonnage; } return $result; } // 检查特殊规则(同步前端所有25条规则) function checkSpecialRules($system, $configItem, $extractedValues, $allText) { $note = ''; $pass = true; $allTextLower = mb_strtolower($allText, 'UTF-8'); $powerType = $extractedValues['动力类型'] ?? ''; $engineType = $extractedValues['发动机型号'] ?? ''; $year = $extractedValues['年款'] ?? ''; // 规则1: 3E22YG51发动机电气系统必须含OPS if ($system == '电气' && strpos($allTextLower, '3e22yg51') !== false) { if ($configItem && strpos($configItem['规格'], 'OPS') === false) { $note .= '规则1:3E22YG51发动机电气系统必须含OPS配置,当前配置无'; $pass = false; } else if ($configItem) { $note .= '符合规则1:3E22YG51发动机电气系统含OPS配置'; } } // 规则2: 16款FD铸造桥涉及转向桥、车架 if (in_array($system, ['转向桥', '车架']) && strpos($allTextLower, '铸造') !== false && $year === '16' && $powerType === 'FD') { if ($configItem && strpos($configItem['规格'], '铸造') === false) { $note .= '规则2:16款FD铸造桥需转向桥/车架含铸造配置,当前配置无'; $pass = false; } else if ($configItem) { $note .= '符合规则2:16款FD铸造桥转向桥/车架含铸造配置'; } } // 规则3: 17款FD铸造桥涉及转向桥、车架、液压管路 if (in_array($system, ['转向桥', '车架', '液压管路']) && strpos($allTextLower, '铸造') !== false && $year === '17' && $powerType === 'FD') { if ($configItem && strpos($configItem['规格'], '铸造') === false) { $note .= '规则3:17款FD铸造桥需转向桥/车架/液压管路含铸造配置,当前配置无'; $pass = false; } else if ($configItem) { $note .= '符合规则3:17款FD铸造桥转向桥/车架/液压管路含铸造配置'; } } // 规则4: 仅指定发动机有加速系统 if ($system == '加速') { $supportedEngines = CONFIG_VALUES['有加速发动机']; if ($engineType && !in_array($engineType, $supportedEngines)) { $note .= '规则4:当前发动机型号无加速系统,无需匹配'; $pass = false; } else if ($configItem) { $note .= '符合规则4:当前发动机型号支持加速系统'; } } // 规则5: 传动冷却只区分吨位,全系通用 if ($system == '传动冷却' && in_array($powerType, ['FD', 'FG', 'FCG'])) { $note .= '符合规则5:传动冷却只区分吨位,全系通用'; $pass = true; } // 规则6-1: 16款4D29X41-024 OPS涉及车架、多路阀、多路阀操纵 if (in_array($system, ['车架', '多路阀系统', '多路阀操纵']) && strpos($allTextLower, 'ops') !== false && $year === '16' && $engineType === '4D29X41-024') { if (($configItem && strpos($configItem['规格'], 'OPS') === false) || !$configItem) { $note .= '规则6-1:16款4D29X41-024 OPS需车架/多路阀含OPS配置,当前无'; $pass = false; } else if ($configItem) { $note .= '符合规则6-1:16款4D29X41-024 OPS车架/多路阀含OPS配置'; } } // 规则6-2: 16款4JG2 OPS涉及车架、多路阀、多路阀操纵、液压管路 if (in_array($system, ['车架', '多路阀系统', '多路阀操纵', '液压管路']) && strpos($allTextLower, 'ops') !== false && $year === '16' && $engineType === '4JG2') { if (($configItem && strpos($configItem['规格'], 'OPS') === false) || !$configItem) { $note .= '规则6-2:16款4JG2 OPS需车架/多路阀/液压管路含OPS配置,当前无'; $pass = false; } else if ($configItem) { $note .= '符合规则6-2:16款4JG2 OPS车架/多路阀/液压管路含OPS配置'; } } // 规则6-3: 16款K21/K25 OPS涉及车架、多路阀、液压管路 if (in_array($system, ['车架', '多路阀系统', '液压管路']) && strpos($allTextLower, 'ops') !== false && $year === '16' && in_array($engineType, ['K21', 'K25'])) { if (($configItem && strpos($configItem['规格'], 'OPS') === false) || !$configItem) { $note .= '规则6-3:16款K21/K25 OPS需车架/多路阀/液压管路含OPS配置,当前无'; $pass = false; } else if ($configItem) { $note .= '符合规则6-3:16款K21/K25 OPS车架/多路阀/液压管路含OPS配置'; } } // 规则6-4: 17款FD/FG非欧五OPS涉及多路阀、多路阀操纵 if (in_array($system, ['多路阀系统', '多路阀操纵']) && strpos($allTextLower, 'ops') !== false && $year === '17' && in_array($powerType, ['FD', 'FG']) && $engineType !== '3E22YG51') { if (($configItem && strpos($configItem['规格'], 'OPS') === false) || !$configItem) { $note .= '规则6-4:17款FD/FG非欧五OPS需多路阀含OPS配置,当前无'; $pass = false; } else if ($configItem) { $note .= '符合规则6-4:17款FD/FG非欧五OPS多路阀含OPS配置'; } } // 规则6-5: 17款欧五OPS涉及多路阀、多路阀操纵、电气 if (in_array($system, ['多路阀系统', '多路阀操纵', '电气']) && strpos($allTextLower, 'ops') !== false && $year === '17' && $powerType === 'FD' && $engineType === '3E22YG51') { if (($configItem && strpos($configItem['规格'], 'OPS') === false) || !$configItem) { $note .= '规则6-5:17款欧五OPS需多路阀/电气含OPS配置,当前无'; $pass = false; } else if ($configItem) { $note .= '符合规则6-5:17款欧五OPS多路阀/电气含OPS配置'; } } // 规则6-6: FB OPS涉及多路阀,带刷卡仪表还涉及电器 if (($system == '多路阀系统' && $powerType === 'FB' && strpos($allTextLower, 'ops') !== false) || ($system == '电器' && $powerType === 'FB' && strpos($allTextLower, 'ops') !== false && strpos($allTextLower, '刷卡仪表') !== false)) { if (($configItem && strpos($configItem['规格'], 'OPS') === false) || !$configItem) { $note .= '规则6-6:FB OPS需多路阀/电器含OPS配置,当前无'; $pass = false; } else if ($configItem) { $note .= '符合规则6-6:FB OPS多路阀/电器含OPS配置'; } } // 规则6-7: FLB OPS涉及多路阀 if ($system == '多路阀系统' && $powerType === 'FLB' && strpos($allTextLower, 'ops') !== false) { if (($configItem && strpos($configItem['规格'], 'OPS') === false) || !$configItem) { $note .= '规则6-7:FLB OPS需多路阀含OPS配置,当前无'; $pass = false; } else if ($configItem) { $note .= '符合规则6-7:FLB OPS多路阀含OPS配置'; } } // 规则:年款不匹配防御 if (in_array($year, ['16', '17']) && $configItem && (strpos($configItem['规格'], '16') !== false || strpos($configItem['规格'], '17') !== false) && strpos($configItem['规格'], $year) === false) { $note .= '防御规则:配置年款与提取年款不匹配,禁止输出'; $pass = false; } else if (in_array($year, ['16', '17']) && $configItem && strpos($configItem['规格'], $year) !== false) { $note .= '防御规则:配置年款与提取年款匹配'; } // 规则7: 高排气涉及覆盖件、排气、平衡重(C240左高,其他右高) if (in_array($system, ['覆盖件', '排气', '平衡重']) && strpos($allTextLower, '高排气') !== false) { $target = $engineType === 'C240' ? '左高排气' : '右高排气'; if (($configItem && strpos($configItem['规格'], $target) === false) || !$configItem) { $note .= "规则7:高排气需{$target}配置,当前无"; $pass = false; } else if ($configItem) { $note .= "符合规则7:高排气含{$target}配置"; } } // 规则8: 无座椅指定且非FR4D/FRB/软连接,默认丰田样式座椅(STD) if ($system == '座椅' && strpos($allTextLower, '座椅') === false && !in_array($powerType, ['FR4D', 'FRB']) && strpos($allTextLower, '软连接') === false) { $note .= '符合规则8:无座椅指定,默认丰田样式座椅(STD)'; $pass = true; } // 规则9: 加大水箱/十叶风扇/油水分离涉及动力冷却 if ($system == '动力冷却' && (strpos($allTextLower, '加大水箱') !== false || strpos($allTextLower, '十叶风扇') !== false || strpos($allTextLower, '油水分离') !== false)) { if (($configItem && (strpos($configItem['规格'], '加大水箱') === false && strpos($configItem['规格'], '十叶风扇') === false && strpos($configItem['规格'], '油水分离') === false)) || !$configItem) { $note .= '规则9:加大水箱/十叶风扇/油水分离需动力冷却配置,当前无'; $pass = false; } else if ($configItem) { $note .= '符合规则9:动力冷却含加大水箱/十叶风扇/油水分离配置'; } } // 规则10: FLB电控维德S2/G1有动力冷却,其他无 if ($system == '动力冷却' && $powerType === 'FLB') { if (strpos($allTextLower, '维德s2') !== false || strpos($allTextLower, '维德g1') !== false) { $note .= '符合规则10:FLB维德S2/G1有动力冷却'; $pass = true; } else { $note .= '规则10:FLB非维德S2/G1无动力冷却,无需匹配'; $pass = false; } } // 规则11: 消声器/中排气涉及排气系统 if ($system == '排气' && (strpos($allTextLower, '消声器') !== false || strpos($allTextLower, '中排气') !== false)) { if (($configItem && (strpos($configItem['规格'], '消声器') === false && strpos($configItem['规格'], '中排气') === false)) || !$configItem) { $note .= '规则11:消声器/中排气需排气系统配置,当前无'; $pass = false; } else if ($configItem) { $note .= '符合规则11:排气系统含消声器/中排气配置'; } } // 规则12: 驾驶室涉及护顶架、覆盖件 if (in_array($system, ['护顶架', '覆盖件']) && strpos($allTextLower, '驾驶室') !== false) { if (($configItem && strpos($configItem['规格'], '驾驶室') === false) || !$configItem) { $note .= '规则12:驾驶室需护顶架/覆盖件含驾驶室配置,当前无'; $pass = false; } else if ($configItem) { $note .= '符合规则12:护顶架/覆盖件含驾驶室配置'; } } // 规则13: 凡几电控驻车制动默认手刹开关 if ($system == '驻车制动' && strpos($allTextLower, '凡几') !== false) { if ($configItem && strpos($configItem['规格'], '手刹开关') === false) { $note .= '规则13:凡几电控驻车制动需手刹开关,当前无'; $pass = false; } else if ($configItem) { $note .= '符合规则13:凡几电控驻车制动含手刹开关'; } } // 规则14: 17款欧五/维德等驻车制动默认手刹开关 if ($system == '驻车制动' && (strpos($allTextLower, '3e22yg51') !== false || strpos($allTextLower, '维德') !== false || strpos($allTextLower, '2403') !== false || strpos($allTextLower, 'wg2503') !== false || strpos($allTextLower, 'psi') !== false || strpos($allTextLower, 'l4crtv') !== false)) { if ($configItem && strpos($configItem['规格'], '手刹开关') === false) { $note .= '规则14:17款欧五/维德等驻车制动需手刹开关,当前无'; $pass = false; } else if ($configItem) { $note .= '符合规则14:17款欧五/维德等驻车制动含手刹开关'; } } // 规则15: 欧五/久保田等覆盖件默认下进气 if ($system == '覆盖件' && (strpos($allTextLower, '3e22yg51') !== false || strpos($allTextLower, 'wg2503-l') !== false || strpos($allTextLower, 'v2403') !== false || strpos($allTextLower, 'psi') !== false || strpos($allTextLower, 'l4crtv') !== false)) { if ($configItem && strpos($configItem['规格'], '下进气') === false) { $note .= '规则15:欧五/久保田等覆盖件需下进气,当前无'; $pass = false; } else if ($configItem) { $note .= '符合规则15:欧五/久保田等覆盖件含下进气'; } } // 规则16: 欧五发动机手刹未拉提醒需断电手刹报警 if ($system == '电气' && strpos($allTextLower, '3e22yg51') !== false && strpos($allTextLower, '手刹未拉') !== false) { if ($configItem && strpos($configItem['规格'], '断电手刹报警') === false) { $note .= '规则16:欧五发动机手刹未拉提醒需断电手刹报警,当前无'; $pass = false; } else if ($configItem) { $note .= '符合规则16:欧五发动机电气含断电手刹报警'; } } // 规则17: Inmotion电控手刹未拉提醒需断电手刹报警 if ($system == '电气' && strpos($allTextLower, 'inmotion') !== false && strpos($allTextLower, '手刹未拉') !== false) { if ($configItem && strpos($configItem['规格'], '断电手刹报警') === false) { $note .= '规则17:Inmotion电控手刹未拉提醒需断电手刹报警,当前无'; $pass = false; } else if ($configItem) { $note .= '符合规则17:Inmotion电控电气含断电手刹报警'; } } // 规则18: 格拉默座椅涉及覆盖件、座椅 if (in_array($system, ['覆盖件', '座椅']) && strpos($allTextLower, '格拉默') !== false) { if ($configItem && strpos($configItem['规格'], '格拉默') === false) { $note .= '规则18:格拉默座椅需覆盖件/座椅含格拉默配置,当前无'; $pass = false; } else if ($configItem) { $note .= '符合规则18:覆盖件/座椅含格拉默配置'; } } // 规则19: 刷卡仪表需TSG81 if ($system == '电器' && strpos($allTextLower, '刷卡') !== false) { if ($configItem && strpos($configItem['规格'], 'TSG81') === false) { $note .= '规则19:刷卡仪表需电器含TSG81配置,当前无'; $pass = false; } else if ($configItem) { $note .= '符合规则19:电器含TSG81配置'; } } // 规则20: 仪表显示P需手刹开关 if ($system == '驻车制动' && strpos($allTextLower, '仪表显示') !== false) { if ($configItem && strpos($configItem['规格'], '手刹开关') === false) { $note .= '规则20:仪表显示P需驻车制动含手刹开关,当前无'; $pass = false; } else if ($configItem) { $note .= '符合规则20:驻车制动含手刹开关'; } } // 规则21: 电瓶挡板涉及覆盖件 if ($system == '覆盖件' && strpos($allTextLower, '电瓶挡板') !== false) { if ($configItem && strpos($configItem['规格'], '电瓶挡板') === false) { $note .= '规则21:电瓶挡板需覆盖件含电瓶挡板配置,当前无'; $pass = false; } else if ($configItem) { $note .= '符合规则21:覆盖件含电瓶挡板配置'; } } // 规则22: BATI涉及平衡重 if ($system == '平衡重' && strpos($allTextLower, 'bati') !== false) { if ($configItem && strpos($configItem['规格'], ' BATI') === false) { $note .= '规则22:BATI需平衡重含BATI配置,当前无'; $pass = false; } else if ($configItem) { $note .= '符合规则22:平衡重含BATI配置'; } } // 规则23: FLB方向盘涉及转向系统 if ($system == '转向系统' && strpos($allTextLower, 'flb方向') !== false) { if (($configItem && strpos($configItem['规格'], 'FLB方向') === false) || !$configItem) { $note .= '规则23:FLB方向盘需转向系统含FLB方向配置,当前无'; $pass = false; } else if ($configItem) { $note .= '符合规则23:转向系统含FLB方向配置'; } } // 规则24: 半交流涉及电器、电气系统 if (in_array($system, ['电器', '电气']) && strpos($allTextLower, '半交流') !== false) { if (($configItem && strpos($configItem['规格'], '半交流') === false) || !$configItem) { $note .= '规则24:半交流需电器/电气含半交流配置,当前无'; $pass = false; } else if ($configItem) { $note .= '符合规则24:电器/电气含半交流配置'; } } // 规则25: 欧五发动机默认进口多路阀 if ($system == '多路阀系统' && strpos($allTextLower, '3e22yg51') !== false && strpos($allTextLower, '进口多路阀') !== false) { if (($configItem && strpos($configItem['名称'], '进口') === false) || !$configItem) { $note .= '规则25:欧五发动机需进口多路阀,当前配置非进口'; $pass = false; } else if ($configItem) { $note .= '符合规则25:欧五发动机多路阀为进口品牌'; } } return ['pass' => $pass, 'note' => $note]; } ?>
2025年07月15日
4 阅读
0 评论
0 点赞
2025-04-06
分班逻辑梳理
<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>主要功能这是一个生产排班系统,能够:读取Excel文件中的生产数据按日期对产品进行分组将每个日期的生产任务智能分配到A班和B班尽量平衡两班的工作量(按产品数量)显示分配结果核心函数解析processFile() - 文件处理入口获取用户上传的Excel文件使用FileReader读取文件内容调用XLSX库解析Excel数据转换数据格式并调用分配逻辑splitProduction(data) - 核心分配逻辑按日期分组:groupedByDate对每个日期的产品按数量降序排序使用贪心算法分配:始终将当前产品分配给总数量较少的班次保持两班总数量尽可能平衡displayResult(data) - 结果展示为每个日期创建表格显示:A班和B班各自的产品列表两班的总数量对比产品种类数统计formatExcelDate() - 日期格式转换将Excel的序列日期值(如44197)转换为"YYYY-MM-DD"格式Excel日期是从1900年1月1日开始的天数数据结构输入数据格式:[[产品ID, 数量, 日期], ...]分组后结构:{ 日期: [{productId, quantity}, ...] }输出结果结构:{ 日期: { A:[], B:[], sumA, sumB, typesA, typesB } }算法特点使用贪心算法实现近似最优分配先排序再分配,确保大任务优先处理平衡两班总工作量,同时统计产品种类使用说明用户上传包含生产数据的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)(主要来自排序操作)。
2025年04月06日
5 阅读
0 评论
0 点赞
2025-04-03
自动排产实现
测试数据排产测试数据.xlsx分班测试.xlsx排产测试数据结果.xlsx排产逻辑:将缺件的产品先按缺件时间先后,再按数量从少到多的顺序进行排列,优先完成同一缺件时间的小批量订单。然后将无法完成的订单递延到第二天继续生产,仍然是优先完成同一缺件时间的小批量订单,检查排产日期是否超过交付时间,排产时间需要早交付时间一天,否则预警.考虑种类和难易程度对产量影响,建立内外嵌套循环,当种类超过阈值则自动终止,若未超过阈值则会排满
2025年04月03日
13 阅读
0 评论
0 点赞
2025-04-01
代码参数修改
function displaySchedule() { const tbody = document.querySelector('#scheduleTable tbody'); tbody.innerHTML = ''; // 按生产日期分组显示 const productionGroups = [...new Set(scheduleData.map(item => item.productionDate))].sort((a, b) => new Date(a) - new Date(b) ); console.log("fest",productionGroups); for (const productionDate of productionGroups) { // 获取该生产日期的所有排产记录 const productions = scheduleData.filter(item => item.productionDate === productionDate); // 检查该生产日期是否有超期订单 const hasLateOrders = productions.some(item => item.isLate); // 添加生产日期标题行 const headerRow = document.createElement('tr'); headerRow.style.backgroundColor = '#f0f0f0'; headerRow.innerHTML = ` <td colspan="6"> <strong>生产日期: ${productionDate}</strong> ${hasLateOrders ? '<span class="warning"> (有超期订单)</span>' : '<span class="on-time"> (全部按时)</span>'} </td> `; tbody.appendChild(headerRow); // 添加该生产日期的所有排产记录 for (const production of productions) { // 获取该生产记录对应的订单 const order = rawData.find(item => item.productId === production.productId && item.deliveryDate === production.deliveryDate ); if (order) { const row = document.createElement('tr'); if (production.isLate) { row.classList.add('warning'); } row.innerHTML = ` <td>${production.productId}</td> <td>${production.quantity}</td> <td>${production.deliveryDate}</td> <td>${production.dailyMax}</td> <td>${production.productionDate}</td> <td>${production.isLate ? '是' : '否'}</td> `; tbody.appendChild(row); } } } }调用API模块代码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 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; } fetch('https://ytpmc.anhuihym.top/pmc/schedule.php', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ data: rawData, workers: workers, productivity: productivity, startDate: startDateStr }) }) .then(response => response.json()) .then(result => { if (result.success) { console.log("排产数据:", result.scheduleData); console.log("预警数据:", result.warningData); displaySchedule(result.scheduleData); displayWarnings(result.warningData); updateSummary(); // 5. 显示结果 displaySchedule(); displayWarnings(); updateSummary(); } else { alert('排产失败: ' + result.message); } }) .catch(error => { console.error("调用排产API出错:", error); alert('调用排产API出错: ' + error.message); }) .finally(() => { btn.disabled = false; loading.style.display = 'none'; }); } catch (error) { console.error("生成排产计划时出错:", error); alert('生成排产计划时出错: ' + error.message); btn.disabled = false; loading.style.display = 'none'; } }, 100); }函数概述displaySchedule() 函数负责将排产计划数据 scheduleData 显示在 HTML 表格中。它首先清空表格的 tbody 部分,然后根据生产日期对数据进行分组,并为每个组添加标题行和相应的排产记录行。外部变量依赖scheduleData:包含排产计划数据的数组。每个元素应包含 productionDate, productId, quantity, deliveryDate, dailyMax, 和 isLate 等属性。rawData:包含原始订单数据的数组。每个元素应包含 productId 和 deliveryDate 等属性,用于与 scheduleData 中的记录进行匹配。函数逻辑清空表格:使用 tbody.innerHTML = ''; 清空表格的 tbody 部分。按生产日期分组:使用 Set 和 map 方法获取唯一的生产日期列表,并按日期排序。遍历每个生产日期:为每个生产日期创建一个标题行,并根据是否有超期订单添加相应的警告信息。遍历该生产日期的所有排产记录:为每个排产记录创建一个表格行,并根据是否超期添加警告类。匹配订单数据:在创建排产记录行时,从 rawData 中找到对应的订单数据,以获取更详细的信息(虽然在当前函数实现中并未直接使用这些信息,但可能是为了后续扩展)。导致无法显示的主要原因是后端赋值时被全局变量作用域污染导致仍然时空数组、修复后如下if (result.success) { // 深拷贝数据(如需独立性) scheduleData = JSON.parse(JSON.stringify(result.scheduleData)); warningData = JSON.parse(JSON.stringify(result.warningData)); // 使用全局变量渲染 displaySchedule(scheduleData); displayWarnings(warningData); updateSummary(); }
2025年04月01日
30 阅读
0 评论
0 点赞
2025-03-26
自动排产最终版本
<!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>
2025年03月26日
17 阅读
0 评论
0 点赞
1
2
0:00