diff --git a/CLAUDE.md b/CLAUDE.md
index c9f2a6b..7fc82a3 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -65,17 +65,33 @@ Model String → Parser → Extractor → Conditions Dictionary → Matcher →
## Data Sheets Structure
-The Excel workbook contains two key sheets:
-
-**领料配置** (Picking Configuration)
-- Defines category hierarchy
-- Specifies which materials require picking
-- Columns: 物料代号, 类别名称, 上层类别名称
+The Excel workbook contains one main configuration sheet:
**平台配置清单** (Platform Configuration)
-- Complete material database
-- Rows 1-3: Headers
-- Row 4+: Data
+- Complete material database with category hierarchy
+- All configuration data in one sheet (simplified from previous two-sheet design)
+- Row 1-3: Headers and product info
+- Row 4+: Material data
+
+**Column Structure** (Row 3):
+| Column | Field Name | Description | Example |
+|--------|-----------|-------------|---------|
+| A | 行号 | Line number | 10, 20, 30 |
+| B | 模块 | Module name | 316L部件 |
+| C | 代号 | Material code | 01011009557 |
+| D | 名称 | Material name | 径向低压接头部件 |
+| E | 数量 | Quantity | 1.0 |
+| F | 选择条件 | Material selection condition | gclj=M20 AND jycz=1 |
+| G | 备注 | Remarks | |
+| H | 类别 | Category name | 部件 |
+| I | 上层类别 | Parent category name | (empty for root categories) |
+| J | 类别选用条件 | Category selection condition | lcfw=M02 |
+| K | 66代码 | 66-system code | 66021009557 |
+
+**Category Selection Condition (类别选用条件)**:
+- Empty: Category is always required
+- Not empty: Category is required only when the condition matches the model specifications
+- Uses same syntax as material selection conditions (AND, OR, !=, parentheses)
## Testing
@@ -120,7 +136,11 @@ Run tests from the VBA editor or Excel macros:
## Key Business Logic
- Materials have "selection conditions" expressed as logical formulas
-- Not all materials in platform config require picking (filtered by "领料配置" sheet)
+- Categories have "category selection conditions" to determine if they are required for a given model
+- **Completeness Check Logic**:
+ - First, determine which categories are required by matching "类别选用条件" with model specifications
+ - Then, check if each required category has exactly one matching material
+ - Categories not required for the model are excluded from completeness check
- Supports "部件" (component) vs "零件" (part) picking strategies
- Condition extraction is data-driven (new variables can be added)
- Category tree can have arbitrary depth
diff --git a/VBA/ClassModules/clsBOMManager.cls b/VBA/ClassModules/clsBOMManager.cls
index cbea360..c60e885 100644
--- a/VBA/ClassModules/clsBOMManager.cls
+++ b/VBA/ClassModules/clsBOMManager.cls
@@ -31,33 +31,44 @@ End Sub
' ========================================
' LoadData 方法
-' 功能: 从Excel工作表加载BOM数据并构建数据结构
+' 功能: 从[平台配置清单]工作表加载BOM数据并构建数据结构
' 参数:
-' wsConfig - [领料配置]工作表对象,包含类别层级和需要领料的物料
-' wsPlatform - [平台配置清单]工作表对象,包含完整的物料信息(代号/名称/数量/条件)
+' wsPlatform - [平台配置清单]工作表对象,包含完整的物料信息和类别配置
+' 表结构: 行号|模块|代号|名称|数量|选择条件|备注|类别|上层类别|类别选用条件|66代码
+' 数据从第4行开始(前3行是标题)
' 处理步骤:
-' 1. 从[平台配置清单]加载所有物料的基础信息到dictAllMaterials
-' 2. 从[领料配置]加载类别信息,筛选需要领料的物料
-' 3. 建立类别的父子关系,构建层级树
-' 注意:
-' - 只有出现在[领料配置]中的物料才会被添加到类别中
-' - 未在[领料配置]中的物料表示不需要领料
+' 1. 从[平台配置清单]一次性加载所有物料信息和类别信息
+' 2. 建立类别的父子关系,构建层级树
+'
+' 重要说明:
+' - 所有数据都从[平台配置清单]读取,不再需要[领料配置]表
+' - 只有"类别"字段不为空的物料才会被添加到类别中
+' - "类别选用条件"用于判断该类别是否需要(为空表示总是需要)
+' - "66代码"是66系统使用的代码
' ========================================
-Public Sub LoadData(wsConfig As Worksheet, wsPlatform As Worksheet)
+Public Sub LoadData(wsPlatform As Worksheet)
Dim i As Long, lastRow As Long
Dim mat As clsMaterialItem
Dim cat As clsCategory
' ========================================
- ' 第一步: 从平台配置清单加载所有物料的基础信息
+ ' 第一步: 从平台配置清单加载所有物料和类别信息
' 说明:
- ' - 读取C列(代号)、D列(名称)、E列(数量)、F列(选择条件)
+ ' 读取列说明:
+ ' - C列(代号)、D列(名称)、E列(数量)、F列(选择条件)
+ ' - H列(类别)、I列(上层类别)、J列(类别选用条件)、K列(66代码)
' - 从第4行开始读取(前3行是标题)
- ' - 所有物料存入dictAllMaterials字典,以代号为键
- ' 目的: 建立完整的物料信息库,供后续按代号查询
+ '
+ ' 处理逻辑:
+ ' 1. 所有物料都存入dictAllMaterials字典(包括无类别的物料)
+ ' 2. 只有"类别"字段不为空的物料才创建类别并添加到类别中
+ ' 3. 类别对象保存CategorySelectCondition属性
+ ' 4. 物料对象保存CategorySelectCondition和Code66属性
' ========================================
lastRow = wsPlatform.Cells(wsPlatform.Rows.Count, "C").End(xlUp).row
+
For i = 4 To lastRow ' 从第4行开始(跳过标题)
+ ' 1. 创建物料对象并读取基础信息
Set mat = New clsMaterialItem
mat.code = Trim(wsPlatform.Cells(i, "C").value & "") ' 物料代号
mat.Name = Trim(wsPlatform.Cells(i, "D").value & "") ' 物料名称
@@ -66,55 +77,46 @@ Public Sub LoadData(wsConfig As Worksheet, wsPlatform As Worksheet)
On Error GoTo 0
mat.Condition = Trim(wsPlatform.Cells(i, "F").value & "") ' 选择条件
- ' 只保存代号非空的物料
+ ' 2. 读取类别相关字段
+ Dim catName As String
+ Dim parentCatName As String
+ Dim catSelectCond As String
+ Dim code66 As String
+
+ catName = Trim(wsPlatform.Cells(i, "H").value & "") ' 类别
+ parentCatName = Trim(wsPlatform.Cells(i, "I").value & "") ' 上层类别
+ catSelectCond = Trim(wsPlatform.Cells(i, "J").value & "") ' 类别选用条件
+ code66 = Trim(wsPlatform.Cells(i, "K").value & "") ' 66代码
+
+ ' 3. 保存类别相关属性到物料对象
+ mat.Category = catName
+ mat.ParentCategory = parentCatName
+ mat.CategorySelectCondition = catSelectCond
+ mat.Code66 = code66
+
+ ' 4. 将物料保存到字典(所有物料都保存,包括无类别的)
If mat.code <> "" Then
Set dictAllMaterials(mat.code) = mat
End If
+
+ ' 5. 如果物料有类别,创建或更新类别对象,并将物料添加到类别
+ If catName <> "" Then
+ ' 确保类别对象存在(如果类别不存在则创建)
+ If Not dictCategories.Exists(catName) Then
+ Set cat = New clsCategory
+ cat.categoryName = catName
+ cat.ParentCategoryName = parentCatName
+ cat.CategorySelectCondition = catSelectCond ' ⭐ 设置类别选用条件
+ Set dictCategories(catName) = cat
+ End If
+
+ ' 将物料添加到类别
+ dictCategories(catName).AddMaterial mat
+ End If
Next i
' ========================================
- ' 第二步: 从领料配置加载类别信息并建立层级
- ' 说明:
- ' - 读取A列(代号)、C列(类别)、D列(上层类别)
- ' - 从第2行开始读取(第1行是标题)
- ' - 只有出现在此表中的物料才需要领料
- ' 处理逻辑:
- ' 1. 为每个类别创建clsCategory对象
- ' 2. 从dictAllMaterials中查找对应的物料信息
- ' 3. 将物料添加到对应的类别中
- ' ========================================
- lastRow = wsConfig.Cells(wsConfig.Rows.Count, "A").End(xlUp).row
- For i = 2 To lastRow ' 从第2行开始
- Dim code As String, catName As String, parentCatName As String
- code = Trim(wsConfig.Cells(i, "A").value & "") ' 物料代号
- catName = Trim(wsConfig.Cells(i, "C").value & "") ' 类别名称
- parentCatName = Trim(wsConfig.Cells(i, "D").value & "") ' 上层类别名称
-
- ' 跳过空行
- If code = "" Then GoTo NextRow
-
- ' 确保类别对象存在(如果类别不存在则创建)
- If Not dictCategories.Exists(catName) Then
- Set cat = New clsCategory
- cat.categoryName = catName
- cat.ParentCategoryName = parentCatName
- Set dictCategories(catName) = cat
- End If
-
- ' 将物料添加到类别
- ' 注意: 必须先在dictAllMaterials中查找到完整的物料信息
- If dictAllMaterials.Exists(code) Then
- Set mat = dictAllMaterials(code)
- mat.Category = catName ' 设置物料所属类别
- mat.ParentCategory = parentCatName ' 设置物料的上层类别
- dictCategories(catName).AddMaterial mat ' 将物料添加到类别对象中
- End If
-
-NextRow:
- Next i
-
- ' ========================================
- ' 第三步: 建立类别层级关系
+ ' 第二步: 建立类别层级关系
' 说明:
' - 遍历所有类别,根据ParentCategoryName建立父子关系
' - 如果类别有父类别,将自己添加到父类别的SubCategories中
@@ -662,6 +664,56 @@ Public Function GetMaterialsByCategoryAndModel(modelStr As String, _
Set GetMaterialsByCategoryAndModel = result
End Function
+' ========================================
+' GetRequiredCategories 方法 (私有)
+' 功能: 根据型号条件确定哪些类别是需要的
+' 参数:
+' conditions - 从型号提取的条件字典 (Dictionary对象)
+' 包含如 lcfw, gclj, jycz 等条件变量
+' 返回: Collection对象, 包含所有需要的 clsCategory 对象
+'
+' 判断逻辑:
+' 遍历所有类别,对每个类别调用 IsRequiredForModel(conditions) 方法:
+' - 如果类别选用条件为空 → 该类别总是需要,添加到结果集
+' - 如果类别选用条件不为空 → 用 conditions 匹配
+' - 匹配成功 → 添加到结果集
+' - 匹配失败 → 不添加
+'
+' 示例:
+' 假设型号条件为: lcfw=M16, gclj=M20
+'
+' 类别1: CategorySelectCondition = ""
+' → IsRequiredForModel = True → 添加到结果集
+'
+' 类别2: CategorySelectCondition = "lcfw=M02"
+' → IsRequiredForModel = False → 不添加
+'
+' 类别3: CategorySelectCondition = "gclj=M20"
+' → IsRequiredForModel = True → 添加到结果集
+'
+' 用途:
+' 在 GetValidMaterialsByModel 中,只检查需要的类别是否有物料匹配
+' 不需要的类别不参与完整性检查
+' ========================================
+Private Function GetRequiredCategories(conditions As Object) As collection
+ Dim result As collection
+ Set result = New collection
+
+ ' 遍历所有类别
+ Dim key As Variant
+ For Each key In dictCategories.Keys
+ Dim cat As clsCategory
+ Set cat = dictCategories(key)
+
+ ' 检查该类别是否需要
+ If cat.IsRequiredForModel(conditions) Then
+ result.Add cat
+ End If
+ Next key
+
+ Set GetRequiredCategories = result
+End Function
+
' ========================================
' ParseModelAndExtractConditions 方法
' 功能: 解析型号并返回条件字典(工具方法)
@@ -689,23 +741,41 @@ End Function
' 参数:
' modelStr - 产品型号字符串
' 返回: Collection对象
-' Collection中包含两个元素:
+' Collection中包含三个元素:
' (1) "Materials" - Collection对象,包含所有匹配的 clsMaterialItem 对象
' (2) "IsComplete" - Boolean值,指示物料是否完整
' (3) "MissingCategories" - Collection对象, 包含所有缺失的类别名称字符串
'
-' 完整性判断规则:
-' - 对于每个需要领料的类别(顶层类别或其需要领取的子类别)
-' - 必须有且仅有一个物料被匹配
-' - 如果某个类别有0个或多于1个物料,则视为不完整
+' ⭐ 完整性判断规则(重构后):
+' 第一步: 确定哪些类别是该型号需要的
+' - 遍历所有类别,检查"类别选用条件"
+' - 类别选用条件为空 → 该类别总是需要
+' - 类别选用条件不为空 → 用型号条件匹配,匹配成功才需要
+'
+' 第二步: 只检查需要的类别的完整性
+' - 对于每个需要的类别(顶层类别或其需要领取的子类别)
+' - 必须有且仅有一个物料被匹配
+' - 如果某个需要的类别有0个或多于1个物料,则视为不完整
+' - 不需要的类别不参与完整性检查
'
' 示例:
+' 假设型号为 "YTHN-100.A0.532.M203.M16.Y3" (量程M16, 过程连接M20)
+'
+' 类别1: CategorySelectCondition = "" → 总是需要
+' 类别2: CategorySelectCondition = "lcfw=M02" → 不需要(型号量程是M16)
+' 类别3: CategorySelectCondition = "gclj=M20" → 需要(匹配成功)
+'
+' 只有类别1和类别3需要检查完整性,类别2不检查
+'
+' 调用示例:
' Dim result As Collection
' Set result = bomMgr.GetValidMaterialsByModel("YTHN-100.A0.532.M203.M16.Y3")
' Dim materials As Collection
' Dim isComplete As Boolean
+' Dim missingCats As Collection
' Set materials = result("Materials")
' isComplete = result("IsComplete")
+' Set missingCats = result("MissingCategories")
' ========================================
Public Function GetValidMaterialsByModel(modelStr As String) As collection
Dim result As New collection
@@ -732,17 +802,21 @@ Public Function GetValidMaterialsByModel(modelStr As String) As collection
' 条件匹配器
Dim matcher As New clsConditionMatcher
- ' 遍历所有根类别,检查完整性
+ ' ⭐ 第一步: 获取需要的类别列表
+ Dim requiredCats As collection
+ Set requiredCats = GetRequiredCategories(conditions)
+
+ ' ⭐ 第二步: 只检查需要的类别的完整性
isComplete = True
- Dim rootCat As clsCategory
+ Dim reqCat As clsCategory
Dim i As Long
- For i = 1 To rootCategories.Count
- Set rootCat = rootCategories(i)
+ For i = 1 To requiredCats.Count
+ Set reqCat = requiredCats(i)
' 检查该类别及其子类别的完整性
Dim catResult As Object
- Set catResult = CheckCategoryCompleteness(rootCat, matcher, conditions)
+ Set catResult = CheckCategoryCompleteness(reqCat, matcher, conditions)
' 1. 合并匹配到的物料
Dim mat As clsMaterialItem
@@ -752,7 +826,7 @@ Public Function GetValidMaterialsByModel(modelStr As String) As collection
allMaterials.Add mat
Next mat
- ' 2. 合并缺失的类别 (新增)
+ ' 2. 合并缺失的类别
Dim missingCollection As collection
Set missingCollection = catResult("Missing")
Dim missingCatName As Variant
@@ -769,7 +843,7 @@ Public Function GetValidMaterialsByModel(modelStr As String) As collection
' 返回结果
result.Add allMaterials, "Materials"
result.Add isComplete, "IsComplete"
- result.Add allMissingCats, "MissingCategories" ' 新增返回项
+ result.Add allMissingCats, "MissingCategories"
Set GetValidMaterialsByModel = result
End Function
diff --git a/VBA/ClassModules/clsCategory.cls b/VBA/ClassModules/clsCategory.cls
index 23e4130..2ae7ea1 100644
--- a/VBA/ClassModules/clsCategory.cls
+++ b/VBA/ClassModules/clsCategory.cls
@@ -6,9 +6,10 @@ Option Explicit
Public categoryName As String
Public ParentCategoryName As String
-Public materials As collection ' 存储 clsMaterialItem 对象
-Public SubCategories As collection ' 存储子类别 clsCategory 对象
-Public IsLeafCategory As Boolean ' 是否叶子类别(无子类别)
+Public materials As collection ' 存储 clsMaterialItem 对象
+Public SubCategories As collection ' 存储子类别 clsCategory 对象
+Public IsLeafCategory As Boolean ' 是否叶子类别(无子类别)
+Public CategorySelectCondition As String ' 类别选用条件(用于判断该类别是否需要)
Private Sub Class_Initialize()
Set materials = New collection
@@ -37,4 +38,47 @@ End Function
' 检查是否有子类别
Public Function HasSubCategories() As Boolean
HasSubCategories = (SubCategories.Count > 0)
+End Function
+
+' ========================================
+' IsRequiredForModel 方法
+' 功能: 根据型号条件判断该类别是否需要
+' 参数:
+' conditions - 从型号提取的条件字典 (Dictionary对象)
+' 返回:
+' True - 该类别需要
+' False - 该类别不需要
+'
+' 判断逻辑:
+' 1. 如果 CategorySelectCondition 为空字符串
+' → 该类别总是需要(无条件限制)
+' 2. 如果 CategorySelectCondition 不为空
+' → 使用 clsConditionMatcher 匹配条件
+' → 匹配成功 → 需要
+' → 匹配失败 → 不需要
+'
+' 示例:
+' 假设型号条件为: lcfw=M16, gclj=M20
+'
+' 情况1: CategorySelectCondition = ""
+' → IsRequiredForModel(conditions) = True
+'
+' 情况2: CategorySelectCondition = "lcfw=M16"
+' → IsRequiredForModel(conditions) = True (匹配)
+'
+' 情况3: CategorySelectCondition = "lcfw=M02"
+' → IsRequiredForModel(conditions) = False (不匹配)
+'
+' 情况4: CategorySelectCondition = "gclj=M20 AND jycz=1"
+' → IsRequiredForModel(conditions) = True (匹配)
+' ========================================
+Public Function IsRequiredForModel(conditions As Object) As Boolean
+ If Me.CategorySelectCondition = "" Then
+ ' 选用条件为空,该类别总是需要
+ IsRequiredForModel = True
+ Else
+ ' 需要用 conditions 去匹配 CategorySelectCondition
+ Dim matcher As New clsConditionMatcher
+ IsRequiredForModel = matcher.IsMatch(Me.CategorySelectCondition, conditions)
+ End If
End Function
\ No newline at end of file
diff --git a/VBA/ClassModules/clsMaterialItem.cls b/VBA/ClassModules/clsMaterialItem.cls
index a87aa79..00c7b95 100644
--- a/VBA/ClassModules/clsMaterialItem.cls
+++ b/VBA/ClassModules/clsMaterialItem.cls
@@ -4,9 +4,11 @@
' ========================================
Option Explicit
-Public code As String ' 代号
-Public Name As String ' 名称
-Public Quantity As Double ' 数量
-Public Condition As String ' 选择条件
-Public Category As String ' 类别
-Public ParentCategory As String ' 上层类别
\ No newline at end of file
+Public code As String ' 代号
+Public Name As String ' 名称
+Public Quantity As Double ' 数量
+Public Condition As String ' 选择条件
+Public Category As String ' 类别
+Public ParentCategory As String ' 上层类别
+Public CategorySelectCondition As String ' 类别选用条件(用于判断该类别是否需要)
+Public Code66 As String ' 66系统代码
\ No newline at end of file
diff --git a/reference_docs/clsBOMManager_LoadData流程分析-v2(整合后).md b/reference_docs/clsBOMManager_LoadData流程分析-v2(整合后).md
new file mode 100644
index 0000000..9fcf6ff
--- /dev/null
+++ b/reference_docs/clsBOMManager_LoadData流程分析-v2(整合后).md
@@ -0,0 +1,530 @@
+# clsBOMManager.LoadData 方法流程分析 (v2.0 - 整合后版本)
+
+## 方法概述
+
+`LoadData` 是 `clsBOMManager` 类的核心方法,负责从 [平台配置清单] 工作表加载 BOM 数据并构建完整的层级数据结构。
+
+**v2.0 变更说明**:
+- 移除了 [领料配置] 表的依赖
+- 所有配置数据统一从 [平台配置清单] 读取
+- 新增"类别选用条件"字段,支持动态判断类别是否需要
+- 新增"66代码"字段
+
+## 方法签名
+
+```vba
+Public Sub LoadData(wsPlatform As Worksheet)
+```
+
+### 参数说明
+
+| 参数 | 类型 | 说明 |
+|------|------|------|
+| `wsPlatform` | Worksheet | [平台配置清单]工作表,包含完整的物料信息和类别配置 |
+
+## 整体流程图
+
+```mermaid
+flowchart TD
+ Start([开始 LoadData]) --> Step1[第一步: 从平台配置清单加载所有物料和类别信息]
+ Step1 --> Step2[第二步: 建立类别层级关系]
+ Step2 --> End([结束])
+
+ style Start fill:#e1f5e1
+ style End fill:#ffe1e1
+ style Step1 fill:#e1f0ff
+ style Step2 fill:#fff4e1
+```
+
+## 详细流程分析
+
+### 第一步:从平台配置清单加载所有物料和类别信息
+
+```mermaid
+flowchart TD
+ S1_Start([开始第一步]) --> S1_Init[初始化: 获取最后一行行号]
+ S1_Init --> S1_Loop{循环 i = 4 到 lastRow}
+ S1_Loop -->|i += 1| S1_ReadMat[读取物料基础信息
C列:代号, D列:名称
E列:数量, F列:选择条件]
+ S1_ReadMat --> S1_ReadCat[读取类别信息
H列:类别, I列:上层类别
J列:类别选用条件, K列:66代码]
+ S1_ReadCat --> S1_CreateMat[创建 clsMaterialItem 对象]
+ S1_CreateMat --> S1_SetMatProps[设置物料属性
code, Name, Quantity, Condition
Category, ParentCategory
CategorySelectCondition, Code66]
+ S1_SetMatProps --> S1_SaveMat[保存到 dictAllMaterials]
+ S1_SaveMat --> S1_CheckCat{类别字段
是否为空?}
+ S1_CheckCat -->|否, 有类别| S1_EnsureCat{类别对象
是否存在?}
+ S1_CheckCat -->|是, 无类别| S1_Next
+ S1_EnsureCat -->|不存在| S1_CreateCat[创建新类别对象
设置 categoryName, ParentCategoryName
CategorySelectCondition]
+ S1_EnsureCat -->|已存在| S1_AddMat
+ S1_CreateCat --> S1_AddMat[将物料添加到类别]
+ S1_AddMat --> S1_Next
+ S1_Next --> S1_Loop
+ S1_Loop -->|i > lastRow| S1_End([第一步完成])
+
+ style S1_Start fill:#e1f5e1
+ style S1_End fill:#ffe1e1
+ style S1_CreateCat fill:#bbdefb
+ style S1_AddMat fill:#c8e6c9
+ style S1_CheckCat fill:#fff9c4
+```
+
+**数据来源**:[平台配置清单] 工作表
+
+| 列 | 字段 | 说明 |
+|----|------|------|
+| C | code | 物料代号 |
+| D | Name | 物料名称 |
+| E | Quantity | 物料数量 |
+| F | Condition | 选择条件 |
+| H | Category | 类别名称 |
+| I | ParentCategoryName | 上层类别名称 |
+| J | CategorySelectCondition | 类别选用条件 |
+| K | Code66 | 66系统代码 |
+
+**数据结构**:
+```
+dictAllMaterials: Dictionary
+├── "01011009557" → clsMaterialItem{
+│ code: "01011009557"
+│ Name: "径向低压接头部件"
+│ Quantity: 1.0
+│ Condition: "gclj=M20 AND jycz=1"
+│ Category: "部件"
+│ ParentCategory: ""
+│ CategorySelectCondition: ""
+│ Code66: "66021009557"
+│ }
+└── ...
+
+dictCategories: Dictionary
+├── "部件" → clsCategory{
+│ categoryName: "部件"
+│ ParentCategoryName: ""
+│ CategorySelectCondition: ""
+│ materials: Collection
+│ SubCategories: Collection
+│ }
+└── ...
+```
+
+**关键代码**:
+```vba
+lastRow = wsPlatform.Cells(wsPlatform.Rows.Count, "C").End(xlUp).row
+
+For i = 4 To lastRow
+ ' 1. 创建物料对象并读取基础信息
+ Set mat = New clsMaterialItem
+ mat.code = Trim(wsPlatform.Cells(i, "C").value & "")
+ mat.Name = Trim(wsPlatform.Cells(i, "D").value & "")
+ mat.Quantity = CDbl(wsPlatform.Cells(i, "E").value)
+ mat.Condition = Trim(wsPlatform.Cells(i, "F").value & "")
+
+ ' 2. 读取类别相关字段
+ catName = Trim(wsPlatform.Cells(i, "H").value & "")
+ parentCatName = Trim(wsPlatform.Cells(i, "I").value & "")
+ catSelectCond = Trim(wsPlatform.Cells(i, "J").value & "")
+ code66 = Trim(wsPlatform.Cells(i, "K").value & "")
+
+ ' 3. 保存到物料对象
+ mat.Category = catName
+ mat.ParentCategory = parentCatName
+ mat.CategorySelectCondition = catSelectCond
+ mat.Code66 = code66
+
+ ' 4. 保存到字典(所有物料都保存)
+ If mat.code <> "" Then
+ Set dictAllMaterials(mat.code) = mat
+ End If
+
+ ' 5. 如果有类别,创建或更新类别对象
+ If catName <> "" Then
+ If Not dictCategories.Exists(catName) Then
+ Set cat = New clsCategory
+ cat.categoryName = catName
+ cat.ParentCategoryName = parentCatName
+ cat.CategorySelectCondition = catSelectCond
+ Set dictCategories(catName) = cat
+ End If
+
+ dictCategories(catName).AddMaterial mat
+ End If
+Next i
+```
+
+**与 v1.0 的区别**:
+
+| 特性 | v1.0 (旧版本) | v2.0 (新版本) |
+|------|--------------|--------------|
+| 数据源 | [平台配置清单] + [领料配置] | 仅 [平台配置清单] |
+| 加载步骤 | 3步 (先物料,后类别,再层级) | 2步 (物料类别一起加载,再层级) |
+| 参数 | wsConfig, wsPlatform | wsPlatform |
+| 类别选用条件 | 不支持 | 支持 |
+| 66代码 | 不支持 | 支持 |
+
+---
+
+### 第二步:建立类别层级关系
+
+```mermaid
+flowchart TD
+ S2_Start([开始第二步]) --> S2_Loop{遍历 dictCategories
所有键}
+ S2_Loop -->|下一个键| S2_GetCat[获取类别对象 cat]
+ S2_GetCat --> S2_CheckParent{ParentCategoryName
是否为空?}
+ S2_CheckParent -->|否, 有父类别| S2_ParentExists{父类别
存在?}
+ S2_CheckParent -->|是, 无父类别| S2_AddRoot[添加到 rootCategories
作为根类别]
+ S2_ParentExists -->|是| S2_AddSub[添加到父类别的
SubCategories 集合]
+ S2_ParentExists -->|否| S2_Orphan[父类别不存在
跳过建立关系]
+ S2_AddRoot --> S2_Next
+ S2_AddSub --> S2_Next
+ S2_Orphan --> S2_Next
+ S2_Next --> S2_Loop
+ S2_Loop -->|遍历完成| S2_End([第二步完成])
+
+ style S2_Start fill:#e1f5e1
+ style S2_End fill:#ffe1e1
+ style S2_AddRoot fill:#ffccbc
+ style S2_AddSub fill:#b2dfdb
+```
+
+**数据结构**:
+```
+rootCategories: Collection
+├── "表壳" (根类别)
+│ └── CategorySelectCondition: "" (总是需要)
+├── "部件" (根类别)
+│ └── CategorySelectCondition: "" (总是需要)
+│ └── SubCategories:
+│ ├── "接头" (子类别)
+│ │ └── CategorySelectCondition: "gclj=M20" (条件性需要)
+│ └── "弹性元件" (子类别)
+│ └── CategorySelectCondition: "lcfw=M02" (条件性需要)
+└── "机芯" (根类别)
+ └── CategorySelectCondition: "jycz=1" (条件性需要)
+```
+
+**关键代码**:
+```vba
+For Each key In dictCategories.Keys
+ Set cat = dictCategories(key)
+ If cat.ParentCategoryName <> "" Then
+ If dictCategories.Exists(cat.ParentCategoryName) Then
+ Set parentCat = dictCategories(cat.ParentCategoryName)
+ parentCat.AddSubCategory cat
+ End If
+ Else
+ rootCategories.Add cat, cat.categoryName
+ End If
+Next key
+```
+
+**说明**:此步骤与 v1.0 版本相同,未发生变化。
+
+---
+
+## 数据结构总结
+
+### 类的私有成员变量
+
+```mermaid
+classDiagram
+ class clsBOMManager {
+ -dictCategories: Dictionary
+ -dictAllMaterials: Dictionary
+ -rootCategories: Collection
+ +LoadData(wsPlatform)
+ +GetRootCategories() Collection
+ +GetCategory(categoryName) clsCategory
+ +GetRequiredCategories(conditions) Collection
+ +GetValidMaterialsByModel(modelStr) Collection
+ }
+
+ class clsCategory {
+ +categoryName: String
+ +ParentCategoryName: String
+ +CategorySelectCondition: String
+ +materials: Collection
+ +SubCategories: Collection
+ +IsLeafCategory: Boolean
+ +AddMaterial(mat)
+ +AddSubCategory(cat)
+ +IsRequiredForModel(conditions) Boolean
+ +HasSubCategories() Boolean
+ }
+
+ class clsMaterialItem {
+ +code: String
+ +Name: String
+ +Quantity: Double
+ +Condition: String
+ +Category: String
+ +ParentCategory: String
+ +CategorySelectCondition: String
+ +Code66: String
+ }
+
+ clsBOMManager "1" --> "*" clsCategory : 管理
+ clsCategory "1" --> "*" clsMaterialItem : 包含
+ clsCategory "1" --> "*" clsCategory : 父子关系
+```
+
+### 三个核心数据容器
+
+| 数据容器 | 类型 | 键/索引 | 值 | 用途 |
+|---------|------|---------|-----|------|
+| `dictAllMaterials` | Dictionary | 物料代号 (String) | clsMaterialItem | 快速查找任意物料的完整信息 |
+| `dictCategories` | Dictionary | 类别名称 (String) | clsCategory | 快速查找任意类别 |
+| `rootCategories` | Collection | 索引 (Long) | clsCategory | 遍历完整的类别树结构 |
+
+---
+
+## 流程时序图
+
+```mermaid
+sequenceDiagram
+ participant Caller as 调用者
+ participant LoadData as LoadData方法
+ participant WSPlatform as [平台配置清单]
+ participant DictMat as dictAllMaterials
+ participant DictCat as dictCategories
+ participant RootCats as rootCategories
+
+ Caller->>LoadData: LoadData(wsPlatform)
+
+ Note over LoadData, WSPlatform: 第一步: 加载物料和类别信息
+ LoadData->>WSPlatform: 读取第4-末行
+ WSPlatform-->>LoadData: 返回物料和类别数据
+ loop 每一行数据
+ LoadData->>DictMat: 创建/添加物料 (包含新字段)
+ alt 类别字段不为空
+ LoadData->>DictCat: 类别存在?
+ alt 类别不存在
+ LoadData->>DictCat: 创建新类别(含类别选用条件)
+ end
+ LoadData->>DictCat: 添加物料到类别
+ end
+ end
+
+ Note over LoadData, RootCats: 第二步: 构建层级树
+ loop 遍历所有类别
+ LoadData->>DictCat: 获取类别
+ alt 有父类别
+ LoadData->>DictCat: 添加到父类别的SubCategories
+ else 无父类别
+ LoadData->>RootCats: 添加为根类别
+ end
+ end
+
+ LoadData-->>Caller: 完成
+```
+
+---
+
+## 重要业务规则
+
+### 1. 物料筛选规则
+- ✅ 类别字段不为空的物料 → **需要领料**,会被添加到类别中
+- ❌ 类别字段为空的物料 → **不需要领料**,仅在 `dictAllMaterials` 中保存
+
+### 2. 类别选用条件规则(v2.0 新增)
+- ✅ 类别选用条件为空 → 该类别**总是需要**(无条件限制)
+- ⚡ 类别选用条件不为空 → 需要用型号条件去匹配
+ - 匹配成功 → 该类别需要
+ - 匹配失败 → 该类别不需要
+
+### 3. 类别层级规则
+- 根类别:`ParentCategoryName` 为空字符串 `""`
+- 子类别:`ParentCategoryName` 指向父类别名称
+- 层级深度:无限制(支持任意深度的树形结构)
+
+### 4. 数据一致性
+- 所有物料必须从 [平台配置清单] 读取
+- 类别字段和类别选用条件字段同时存在
+- 一个物料只能属于一个类别
+
+---
+
+## 使用示例
+
+### 调用 LoadData
+
+```vba
+Dim bomMgr As New clsBOMManager
+Dim wsPlatform As Worksheet
+
+Set wsPlatform = ThisWorkbook.Worksheets("平台配置清单")
+
+' 加载数据 (v2.0 只需要一个参数)
+Call bomMgr.LoadData(wsPlatform)
+
+' 获取根类别
+Dim rootCats As Collection
+Set rootCats = bomMgr.GetRootCategories()
+
+' 遍历所有根类别
+Dim i As Long
+For i = 1 To rootCats.Count
+ Debug.Print "根类别: " & rootCats(i).categoryName
+ Debug.Print "类别选用条件: " & rootCats(i).CategorySelectCondition
+Next i
+```
+
+### 使用类别选用条件
+
+```vba
+' 解析型号并提取条件
+Dim conditions As Object
+Set conditions = bomMgr.ParseModelAndExtractConditions("YTHN-100.A0.532.M203.M16.Y3")
+
+' 获取需要的类别
+Dim requiredCats As Collection
+Set requiredCats = bomMgr.GetRequiredCategories(conditions)
+
+' 遍历需要的类别
+Dim cat As clsCategory
+For Each cat In requiredCats
+ Debug.Print "需要的类别: " & cat.categoryName
+Next cat
+```
+
+---
+
+## 执行后的数据结构示例
+
+假设加载后的数据结构如下:
+
+```
+型号: YTHN-100.A0.532.M203.M16.Y3
+提取的条件: lcfw=M16, gclj=M20, jycz=1, azxs=A0
+
+dictAllMaterials:
+{
+ "01011009557" → {
+ code: "01011009557",
+ Name: "径向低压接头部件",
+ Quantity: 1.0,
+ Condition: "gclj=M20 AND jycz=1 AND lcfw=M01",
+ Category: "部件",
+ ParentCategory: "",
+ CategorySelectCondition: "",
+ Code66: "66021009557"
+ }
+}
+
+dictCategories:
+{
+ "部件" → {
+ categoryName: "部件",
+ ParentCategoryName: "",
+ CategorySelectCondition: "",
+ materials: [...],
+ SubCategories: ["接头", "弹性元件"]
+ },
+ "接头" → {
+ categoryName: "接头",
+ ParentCategoryName: "部件",
+ CategorySelectCondition: "gclj=M20", ← 类别选用条件
+ materials: [...],
+ SubCategories: []
+ },
+ "机芯" → {
+ categoryName: "机芯",
+ ParentCategoryName: "",
+ CategorySelectCondition: "jycz=1", ← 类别选用条件
+ materials: [...],
+ SubCategories: []
+ }
+}
+
+判断哪些类别需要:
+- "部件" → CategorySelectCondition="" → 总是需要 ✓
+- "接头" → CategorySelectCondition="gclj=M20" → 型号有gclj=M20 → 需要 ✓
+- "机芯" → CategorySelectCondition="jycz=1" → 型号有jycz=1 → 需要 ✓
+
+requiredCategories = ["部件", "接头", "机芯"]
+```
+
+---
+
+## 错误处理
+
+LoadData 方法本身不包含显式的错误处理(`On Error`),依赖以下机制:
+
+1. **空值处理**:使用 `& ""` 确保字符串转换不会失败
+2. **类型转换**:使用 `CDbl()` 时没有错误处理,假设数据格式正确
+3. **跳过机制**:物料代号为空时,不保存到字典
+4. **存在性检查**:使用 `Dictionary.Exists()` 避免键不存在错误
+
+---
+
+## 相关方法
+
+LoadData 执行后,可以使用以下方法访问数据:
+
+| 方法 | 说明 | v2.0变化 |
+|------|------|---------|
+| `GetRootCategories()` | 获取所有顶层类别 | 无变化 |
+| `GetCategory(categoryName)` | 根据名称获取类别对象 | 无变化 |
+| `GetRequiredCategories(conditions)` | ⭐ 获取需要的类别列表 | 新增 |
+| `GetValidMaterialsByModel()` | 获取物料并检查完整性 | ⭐ 已重构,使用类别选用条件 |
+| `GetMaterialsByModel()` | 根据型号获取物料 | 无变化 |
+| `PrintCategoryTree()` | 打印类别树(调试用) | 无变化 |
+
+---
+
+## 迁移指南 (v1.0 → v2.0)
+
+### 代码变更
+
+**旧代码 (v1.0)**:
+```vba
+Dim wsConfig As Worksheet
+Dim wsPlatform As Worksheet
+Set wsConfig = ThisWorkbook.Worksheets("领料配置")
+Set wsPlatform = ThisWorkbook.Worksheets("平台配置清单")
+
+Call bomMgr.LoadData(wsConfig, wsPlatform) ' 两个参数
+```
+
+**新代码 (v2.0)**:
+```vba
+Dim wsPlatform As Worksheet
+Set wsPlatform = ThisWorkbook.Worksheets("平台配置清单")
+
+Call bomMgr.LoadData(wsPlatform) ' 一个参数
+```
+
+### 数据变更
+
+**旧表结构**:
+- [领料配置]: A列=代号, C列=类别, D列=上层类别
+- [平台配置清单]: C列=代号, D列=名称, E列=数量, F列=选择条件
+
+**新表结构**:
+- [平台配置清单]: C列=代号, D列=名称, E列=数量, F列=选择条件
+ H列=类别, I列=上层类别, J列=类别选用条件, K列=66代码
+
+### 逻辑变更
+
+**完整性检查逻辑**:
+- **v1.0**: 所有根类别都必须匹配到物料
+- **v2.0**: 只检查需要的类别(通过类别选用条件判断)
+
+---
+
+## 总结
+
+`LoadData` 方法(v2.0)通过两个阶段构建完整的 BOM 数据结构:
+
+1. **数据加载**:从 [平台配置清单] 一次性加载所有物料和类别信息(包括新增的类别选用条件和66代码)
+2. **结构构建**:构建类别的树形层级关系
+
+v2.0 的主要改进:
+- ✅ 简化数据源:从一个表读取所有数据
+- ✅ 支持动态类别判断:通过类别选用条件
+- ✅ 更精确的完整性检查:只检查需要的类别
+- ✅ 支持66系统:新增66代码字段
+
+最终形成的数据结构支持:
+- 快速按代号查找物料
+- 快速按类别查找物料
+- 遍历完整的类别树
+- 根据型号动态确定需要的类别
+- 支持父类别/子类别的领料逻辑
diff --git a/reference_docs/平台配置清单.md b/reference_docs/平台配置清单.md
new file mode 100644
index 0000000..2b71a9b
--- /dev/null
+++ b/reference_docs/平台配置清单.md
@@ -0,0 +1,11 @@
+| | A | B | C | D | E | F | G | H | I | J | K |
+| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
+| 1 | 代号 | YTHN-100 | 描述 | | 英文名称 | | | | | | |
+| 2 | 名称 | (不锈钢)耐震压力表 | 负责人 | | 备注 | | | | | | |
+| 3 | 行号 | 模块 | 代号 | 名称 | 数量 | 选择条件 | 备注 | 类别 | 上层类别 | 类别选用条件 | 66代码 |
+| 4 | 10 | 316L部件 | 01011009557 | 径向低压接头部件 | 1.0 | gclj=M20 AND jycz=1 AND lcfw=M01 AND (azxs=A0 OR azxs=AT OR azxs=AH) | | 部件 | | | 66021009557 |
+| 5 | 20 | 316L部件 | 01011009520 | 径向低压接头部件 | 1.0 | gclj=M20 AND jycz=1 AND lcfw=M02 AND (azxs=A0 OR azxs=AT OR azxs=AH) | | 部件 | | | 66021009520 |
+| 6 | 30 | 316L部件 | 01011013536 | 径向低压接头部件 | 1.0 | gclj=M20 AND jycz=1 AND lcfw=M03 AND (azxs=A0 OR azxs=AT OR azxs=AH) | | 部件 | | | 66021013536 |
+| 7 | 40 | 316L部件 | 01011013538 | 径向低压接头部件 | 1.0 | gclj=M20 AND jycz=1 AND lcfw=M04 AND (azxs=A0 OR azxs=AT OR azxs=AH) | | 部件 | | | 66021013538 |
+| 8 | 50 | 316L部件 | 01011009522 | 径向低压接头部件 | 1.0 | gclj=M20 AND jycz=1 AND lcfw=M05 AND (azxs=A0 OR azxs=AT OR azxs=AH) | | 部件 | | | 66021009522 |
+| 9 | 60 | 316L部件 | 01011013336 | 径向低压接头部件 | 1.0 | gclj=M20 AND jycz=1 AND lcfw=M06 AND (azxs=A0 OR azxs=AT OR azxs=AH) | | 部件 | | | 66021013336 |
\ No newline at end of file
diff --git a/reference_docs/重构计划-整合领料配置到平台配置清单.md b/reference_docs/重构计划-整合领料配置到平台配置清单.md
new file mode 100644
index 0000000..99f9a27
--- /dev/null
+++ b/reference_docs/重构计划-整合领料配置到平台配置清单.md
@@ -0,0 +1,650 @@
+# 重构计划:整合 [领料配置] 到 [平台配置清单]
+
+## 一、背景概述
+
+### 1.1 重构目标
+
+将原有的 [领料配置] 表格的功能整合到 [平台配置清单] 中,简化数据结构,统一管理。
+
+### 1.2 变化原因
+
+- 原有设计:物料基础信息在 [平台配置清单],类别配置在 [领料配置],数据分散
+- 新设计:所有信息集中在 [平台配置清单] 一张表,便于维护
+
+---
+
+## 二、新工作表结构分析
+
+### 2.1 新表结构
+
+**[平台配置清单] 表结构**(第3行标题):
+
+| 列 | 字段名 | 说明 | 示例 |
+|---|--------|------|------|
+| A | 行号 | 数据行序号 | 10, 20, 30 |
+| B | 模块 | 模块分类 | 316L部件 |
+| C | 代号 | 物料代号 | 01011009557 |
+| D | 名称 | 物料名称 | 径向低压接头部件 |
+| E | 数量 | 物料数量 | 1.0 |
+| F | 选择条件 | 物料选用条件 | gclj=M20 AND jycz=1 |
+| G | 备注 | 备注信息 | |
+| **H** | **类别** | ⭐ 新增:物料所属类别 | 部件 |
+| **I** | **上层类别** | ⭐ 新增:父类别名称 | |
+| **J** | **类别选用条件** | ⭐ 新增:判断该类别是否需要 | lcfw=M02 |
+| **K** | **66代码** | ⭐ 新增:66系统代码 | 66021009557 |
+
+### 2.2 字段说明
+
+#### 新增字段详解
+
+1. **类别(H列)**
+ - 类型:字符串
+ - 说明:物料所属的类别名称
+ - 示例:`部件`、`表壳`、`接头`、`机芯`
+ - 为空:表示该物料不属于任何类别(不需要领料)
+
+2. **上层类别(I列)**
+ - 类型:字符串
+ - 说明:父类别的名称,用于构建类别层级树
+ - 示例:`接头` 的上层类别是 `部件`
+ - 为空:表示该类别是根类别
+
+3. **类别选用条件(J列)** ⭐ **关键字段**
+ - 类型:字符串(逻辑表达式)
+ - 说明:用于判断该类别是否需要被选用
+ - 格式:与"选择条件"字段相同的逻辑表达式
+ - 判断规则:
+ - **为空**:该类别总是需要(无条件)
+ - **不为空**:需要用型号提取的条件去匹配,匹配成功才需要
+ - 示例:
+ - `lcfw=M02`:只有当量程是 M02 时才需要这个类别
+ - `gclj=M20 AND jycz=1`:只有当过程连接是 M20 且接液材质是 1 时才需要
+
+4. **66代码(K列)**
+ - 类型:字符串
+ - 说明:66系统使用的代码(具体用途待补充)
+ - 示例:`66021009557`
+
+---
+
+## 三、核心业务逻辑变化
+
+### 3.1 旧逻辑(重构前)
+
+```
+有效性检查规则:
+- 所有根类别都必须匹配到至少1个物料
+- 每个根类别(或其子类别)必须有且仅有1个物料匹配
+- 如果有任何一个类别不满足,则物料集合不完整
+```
+
+**问题**:
+- 无法表达"某些型号不需要某些类别"的情况
+- 例如:某些型号可能不需要"机芯"类别
+
+### 3.2 新逻辑(重构后)
+
+```
+有效性检查规则:
+- 第一步:确定哪些类别是该型号需要的
+ - 遍历所有有物料的类别
+ - 如果"类别选用条件"为空 → 该类别总是需要
+ - 如果"类别选用条件"不为空 → 用型号条件去匹配,匹配成功才需要
+
+- 第二步:只检查需要的类别是否有物料匹配
+ - 对于需要的类别,必须有且仅有1个物料匹配
+ - 不需要的类别不参与完整性检查
+```
+
+**示例场景**:
+
+型号:`YTHN-100.A0.532.M203.M16.Y3` (量程 M16)
+
+| 类别 | 类别选用条件 | 是否需要 | 说明 |
+|------|-------------|---------|------|
+| 表壳 | (空) | ✅ 需要 | 无条件,总是需要 |
+| 部件 | lcfw=M02 | ❌ 不需要 | 型号量程是 M16,不满足条件 |
+| 接头 | lcfw=M16 | ✅ 需要 | 型号量程是 M16,满足条件 |
+| 机芯 | gclj=M20 | ❌ 不需要 | 型号过程连接不是 M20 |
+
+**结果**:只需要检查 `表壳` 和 `接头` 类别的物料完整性
+
+---
+
+## 四、代码影响分析
+
+### 4.1 需要修改的模块和方法
+
+#### 1. clsBOMManager 类 ⭐ **核心改动**
+
+**1.1 LoadData 方法**
+
+```vba
+' 旧签名
+Public Sub LoadData(wsConfig As Worksheet, wsPlatform As Worksheet)
+
+' 新签名
+Public Sub LoadData(wsPlatform As Worksheet)
+```
+
+**变化**:
+- ❌ 移除 `wsConfig` 参数(不再需要 [领料配置] 表)
+- ✅ 所有数据从 [平台配置清单] 读取
+- ✅ 读取列数增加:从 A-F 扩展到 A-K
+
+**读取列对应**:
+```vba
+' 第一步:加载物料和类别信息(一次性完成)
+For i = 4 To lastRow
+ ' 基础信息
+ mat.code = Trim(wsPlatform.Cells(i, "C").value) ' 代号
+ mat.Name = Trim(wsPlatform.Cells(i, "D").value) ' 名称
+ mat.Quantity = CDbl(wsPlatform.Cells(i, "E").value) ' 数量
+ mat.Condition = Trim(wsPlatform.Cells(i, "F").value) ' 选择条件
+
+ ' ⭐ 新增:类别相关字段
+ catName = Trim(wsPlatform.Cells(i, "H").value) ' 类别
+ parentCatName = Trim(wsPlatform.Cells(i, "I").value) ' 上层类别
+ catSelectCond = Trim(wsPlatform.Cells(i, "J").value) ' 类别选用条件
+ code66 = Trim(wsPlatform.Cells(i, "K").value) ' 66代码
+Next i
+```
+
+**流程变化**:
+```
+旧流程(三步):
+ 1. 从 [平台配置清单] 加载物料基础信息
+ 2. 从 [领料配置] 加载类别信息
+ 3. 建立类别层级关系
+
+新流程(两步):
+ 1. 从 [平台配置清单] 加载物料和类别信息(一次性)
+ 2. 建立类别层级关系
+```
+
+---
+
+**1.2 GetValidMaterialsByModel 方法**
+
+**变化**:
+- ✅ 需要先判断哪些类别是该型号需要的
+- ✅ 只检查需要的类别的完整性
+
+**新逻辑**:
+```vba
+Public Function GetValidMaterialsByModel(modelStr As String) As collection
+ ' 1. 解析型号并提取条件
+ Set conditions = ParseModelAndExtractConditions(modelStr)
+
+ ' 2. ⭐ 新增:确定需要的类别列表
+ Dim requiredCategories As collection
+ Set requiredCategories = GetRequiredCategories(conditions)
+
+ ' 3. 只检查需要的类别
+ For Each cat In requiredCategories
+ Set catResult = CheckCategoryCompleteness(cat, matcher, conditions)
+ ' ...
+ Next cat
+End Function
+```
+
+---
+
+**1.3 GetRequiredCategories 方法(新增)**
+
+**功能**:根据型号条件,确定哪些类别是需要的
+
+```vba
+Private Function GetRequiredCategories(conditions As Object) As collection
+ ' 遍历所有类别
+ ' - 类别选用条件为空 → 添加到需要列表
+ ' - 类别选用条件不为空 → 用 conditions 去匹配
+ ' - 匹配成功 → 添加到需要列表
+ ' - 匹配失败 → 不添加
+End Function
+```
+
+---
+
+**1.4 CheckCategoryCompleteness 方法**
+
+**变化**:
+- 无需修改,因为调用者已经过滤了不需要的类别
+- 逻辑保持不变:检查需要的类别是否有且仅有1个物料匹配
+
+---
+
+#### 2. clsCategory 类 ⭐ **需要扩展**
+
+**新增属性**:
+
+```vba
+Public CategorySelectCondition As String ' ⭐ 新增:类别选用条件
+Public IsRequired As Boolean ' ⭐ 新增:是否需要(计算属性)
+```
+
+**新增方法**:
+
+```vba
+' 检查该类别是否需要(根据型号条件)
+Public Function IsRequiredForModel(conditions As Object) As Boolean
+ If Me.CategorySelectCondition = "" Then
+ ' 选用条件为空,总是需要
+ IsRequiredForModel = True
+ Else
+ ' 需要用 conditions 去匹配 CategorySelectCondition
+ Dim matcher As New clsConditionMatcher
+ IsRequiredForModel = matcher.IsMatch(Me.CategorySelectCondition, conditions)
+ End If
+End Function
+```
+
+---
+
+#### 3. clsMaterialItem 类 ⭐ **需要扩展**
+
+**新增属性**:
+
+```vba
+Public CategorySelectCondition As String ' ⭐ 新增:类别选用条件
+Public Code66 As String ' ⭐ 新增:66代码
+```
+
+**说明**:
+- 物料需要保存所属类别的"类别选用条件"
+- 便于后续判断该物料是否需要
+
+---
+
+### 4.2 不需要修改的方法
+
+以下方法不受影响,无需修改:
+
+- ✅ `GetRootCategories()` - 获取根类别
+- ✅ `GetCategory(categoryName)` - 获取类别对象
+- ✅ `GetMaterialsByModel()` - 根据型号获取物料(不涉及完整性检查)
+- ✅ `GetMaterialsForPicking()` - 获取领料物料
+- ✅ `ParseModelAndExtractConditions()` - 解析型号
+- ✅ `FilterCategoryWithSubcategories()` - 类别筛选
+- ✅ `CollectAllMaterials()` - 收集物料
+- ✅ `PrintCategoryTree()` - 打印类别树
+
+---
+
+## 五、重构实施计划
+
+### 5.1 实施步骤
+
+#### 阶段一:数据模型扩展(1-2小时)
+
+**任务清单**:
+
+1. ✅ **修改 clsMaterialItem 类**
+ - [ ] 添加 `CategorySelectCondition` 属性
+ - [ ] 添加 `Code66` 属性
+ - [ ] 更新类注释
+
+2. ✅ **修改 clsCategory 类**
+ - [ ] 添加 `CategorySelectCondition` 属性
+ - [ ] 添加 `IsRequiredForModel()` 方法
+ - [ ] 更新类注释
+
+3. ✅ **添加测试用例**
+ - [ ] 测试类别选用条件的匹配逻辑
+ - [ ] 测试空条件和非空条件的情况
+
+---
+
+#### 阶段二:LoadData 方法重构(2-3小时)
+
+**任务清单**:
+
+1. ✅ **修改方法签名**
+ ```vba
+ ' 旧
+ Public Sub LoadData(wsConfig As Worksheet, wsPlatform As Worksheet)
+
+ ' 新
+ Public Sub LoadData(wsPlatform As Worksheet)
+ ```
+
+2. ✅ **重写加载逻辑**
+ - [ ] 移除第二步(从 [领料配置] 加载)
+ - [ ] 合并第一步和第二步(一次性从 [平台配置清单] 加载所有字段)
+ - [ ] 更新列索引映射:
+ - H列:类别
+ - I列:上层类别
+ - J列:类别选用条件
+ - K列:66代码
+
+3. ✅ **更新类别创建逻辑**
+ - [ ] 创建类别时,设置 `CategorySelectCondition` 属性
+ - [ ] 创建物料时,设置 `CategorySelectCondition` 和 `Code66` 属性
+
+4. ✅ **更新文档**
+ - [ ] 更新 `clsBOMManager_LoadData流程分析.md`
+ - [ ] 更新方法注释
+
+---
+
+#### 阶段三:完整性检查逻辑重构(2-3小时)
+
+**任务清单**:
+
+1. ✅ **新增 GetRequiredCategories 方法**
+ ```vba
+ Private Function GetRequiredCategories(conditions As Object) As collection
+ ' 遍历所有类别,判断是否需要
+ ' 返回需要的类别列表
+ End Function
+ ```
+
+2. ✅ **修改 GetValidMaterialsByModel 方法**
+ - [ ] 在检查完整性前,先调用 `GetRequiredCategories()`
+ - [ ] 只对需要的类别调用 `CheckCategoryCompleteness()`
+ - [ ] 更新方法注释
+
+3. ✅ **测试完整性检查**
+ - [ ] 测试所有类别都需要的情况(向后兼容)
+ - [ ] 测试部分类别不需要的情况(新逻辑)
+ - [ ] 测试边界情况(所有类别都不需要)
+
+---
+
+#### 阶段四:测试与验证(2-3小时)
+
+**任务清单**:
+
+1. ✅ **单元测试**
+ - [ ] 测试 `LoadData` 加载新表结构
+ - [ ] 测试 `IsRequiredForModel()` 方法
+ - [ ] 测试 `GetRequiredCategories()` 方法
+ - [ ] 测试 `GetValidMaterialsByModel()` 新逻辑
+
+2. ✅ **集成测试**
+ - [ ] 使用真实数据测试完整流程
+ - [ ] 验证结果与预期一致
+
+3. ✅ **回归测试**
+ - [ ] 运行 `modModelParserTest` 中的所有测试
+ - [ ] 确保旧功能不受影响
+
+---
+
+#### 阶段五:文档更新(1小时)
+
+**任务清单**:
+
+1. ✅ **更新 CLAUDE.md**
+ - [ ] 更新 [平台配置清单] 表结构说明
+ - [ ] 移除 [领料配置] 相关说明
+ - [ ] 更新"类别选用条件"业务逻辑说明
+
+2. ✅ **更新参考文档**
+ - [ ] 更新 `clsBOMManager使用说明.md`
+ - [ ] 更新 `clsBOMManager_LoadData流程分析.md`
+
+3. ✅ **添加迁移指南**
+ - [ ] 创建 `数据迁移指南.md`
+ - [ ] 说明如何将旧数据迁移到新结构
+
+---
+
+### 5.2 风险评估
+
+| 风险项 | 风险等级 | 缓解措施 |
+|--------|---------|---------|
+| 数据结构变化导致现有代码报错 | 🔴 高 | 充分的单元测试和回归测试 |
+| 类别选用条件逻辑理解偏差 | 🟡 中 | 详细的业务场景测试 |
+| 向后兼容性问题 | 🟡 中 | 保留旧版本的 LoadData 方法(重命名为 LoadDataV1) |
+| 性能影响 | 🟢 低 | 新逻辑只增加一次类别过滤,影响极小 |
+
+---
+
+### 5.3 时间估算
+
+| 阶段 | 预计时间 | 负责人 |
+|------|---------|--------|
+| 阶段一:数据模型扩展 | 1-2 小时 | |
+| 阶段二:LoadData 重构 | 2-3 小时 | |
+| 阶段三:完整性检查重构 | 2-3 小时 | |
+| 阶段四:测试与验证 | 2-3 小时 | |
+| 阶段五:文档更新 | 1 小时 | |
+| **总计** | **8-12 小时** | |
+
+---
+
+## 六、数据迁移方案
+
+### 6.1 从旧表到新表
+
+**旧 [领料配置] 结构**:
+| A列 | B列 | C列 | D列 |
+|-----|-----|-----|-----|
+| 物料代号 | 物料名称 | 类别 | 上层类别 |
+
+**新 [平台配置清单] 结构**:
+| ... | H列 | I列 | J列 | K列 |
+|-----|-----|-----|-----|-----|
+| ... | 类别 | 上层类别 | 类别选用条件 | 66代码 |
+
+**迁移步骤**:
+
+1. **在 [平台配置清单] 中添加新列**:
+ - H列:类别
+ - I列:上层类别
+ - J列:类别选用条件
+ - K列:66代码
+
+2. **从 [领料配置] 复制数据**:
+ ```
+ H2 = [领料配置]!A2 ' 物料代号 → 类别(需要匹配)
+ I2 = [领料配置]!C2 ' 类别
+ J2 = [领料配置]!D2 ' 上层类别
+ ```
+
+3. **使用 VLOOKUP 匹配物料**:
+ ```excel
+ H4 = VLOOKUP(C4, [领料配置]!A:D, 3, FALSE) ' 类别
+ I4 = VLOOKUP(C4, [领料配置]!A:D, 4, FALSE) ' 上层类别
+ ```
+
+4. **手动填写类别选用条件和66代码**
+
+---
+
+## 七、测试用例设计
+
+### 7.1 类别选用条件测试
+
+| 测试场景 | 类别选用条件 | 型号条件 | 预期结果 |
+|---------|-------------|---------|---------|
+| 无条件(总是需要) | (空) | 任意 | ✅ 需要 |
+| 量程匹配 | lcfw=M02 | lcfw=M02 | ✅ 需要 |
+| 量程不匹配 | lcfw=M02 | lcfw=M16 | ❌ 不需要 |
+| 复杂条件匹配 | gclj=M20 AND jycz=1 | gclj=M20, jycz=1 | ✅ 需要 |
+| 复杂条件不匹配 | gclj=M20 AND jycz=1 | gclj=M20, jycz=2 | ❌ 不需要 |
+| OR 条件匹配 | lcfw=M02 OR lcfw=M16 | lcfw=M16 | ✅ 需要 |
+
+### 7.2 完整性检查测试
+
+| 测试场景 | 需要的类别 | 匹配结果 | 预期 IsComplete |
+|---------|-----------|---------|-----------------|
+| 全部匹配 | 表壳, 部件, 接头 | 全部有1个 | ✅ True |
+| 缺失一个 | 表壳, 部件, 接头 | 部件缺失 | ❌ False |
+| 部分类别不需要 | 表壳, 部件 | 表壳有, 部件无(但部件不需要) | ✅ True |
+| 多于一个匹配 | 表壳, 部件 | 部件有2个 | ❌ False |
+
+---
+
+## 八、向后兼容性策略
+
+### 8.1 保留旧版本方法
+
+```vba
+' 旧版本(保留但不推荐使用)
+Public Sub LoadDataV1(wsConfig As Worksheet, wsPlatform As Worksheet)
+ ' 保留原有实现
+End Sub
+
+' 新版本(推荐使用)
+Public Sub LoadData(wsPlatform As Worksheet)
+ ' 新实现
+End Sub
+```
+
+### 8.2 迁移期支持
+
+在迁移期间,两个版本同时存在:
+- 新代码使用 `LoadData(wsPlatform)`
+- 旧代码可以继续使用 `LoadDataV1(wsConfig, wsPlatform)`
+
+迁移完成后,可以移除 `LoadDataV1` 方法。
+
+---
+
+## 九、检查清单
+
+### 重构前
+
+- [ ] 备份现有代码
+- [ ] 备份 [领料配置] 和 [平台配置清单] 表
+- [ ] 确认测试环境可用
+
+### 重构中
+
+- [ ] 按阶段顺序执行
+- [ ] 每个阶段完成后进行测试
+- [ ] 及时更新文档
+
+### 重构后
+
+- [ ] 运行完整的测试套件
+- [ ] 验证所有功能正常
+- [ ] 更新用户文档
+- [ ] 通知团队成员变更内容
+
+---
+
+## 十、后续优化建议
+
+1. **性能优化**
+ - 缓存类别选用条件的匹配结果
+ - 避免重复解析型号
+
+2. **功能扩展**
+ - 支持类别优先级(当多个类别都匹配时,优先选择哪个)
+ - 支持类别别名(一个类别多个名称)
+
+3. **用户体验**
+ - 在 Excel 中添加类别选用条件的验证工具
+ - 提供类别选用条件的构建器(图形化界面)
+
+---
+
+## 附录:关键代码示例
+
+### A. 修改后的 LoadData 方法(伪代码)
+
+```vba
+Public Sub LoadData(wsPlatform As Worksheet)
+ Dim i As Long, lastRow As Long
+ Dim mat As clsMaterialItem
+ Dim cat As clsCategory
+
+ ' 读取数据
+ lastRow = wsPlatform.Cells(wsPlatform.Rows.Count, "C").End(xlUp).row
+
+ For i = 4 To lastRow
+ ' 1. 创建物料对象
+ Set mat = New clsMaterialItem
+ mat.code = Trim(wsPlatform.Cells(i, "C").value)
+ mat.Name = Trim(wsPlatform.Cells(i, "D").value)
+ mat.Quantity = CDbl(wsPlatform.Cells(i, "E").value)
+ mat.Condition = Trim(wsPlatform.Cells(i, "F").value)
+
+ ' ⭐ 读取类别相关字段
+ Dim catName As String
+ Dim parentCatName As String
+ Dim catSelectCond As String
+ Dim code66 As String
+
+ catName = Trim(wsPlatform.Cells(i, "H").value & "")
+ parentCatName = Trim(wsPlatform.Cells(i, "I").value & "")
+ catSelectCond = Trim(wsPlatform.Cells(i, "J").value & "")
+ code66 = Trim(wsPlatform.Cells(i, "K").value & "")
+
+ ' ⭐ 保存到物料对象
+ mat.Category = catName
+ mat.ParentCategory = parentCatName
+ mat.CategorySelectCondition = catSelectCond
+ mat.Code66 = code66
+
+ ' 保存到字典
+ If mat.code <> "" Then
+ Set dictAllMaterials(mat.code) = mat
+ End If
+
+ ' ⭐ 创建或更新类别对象
+ If catName <> "" Then
+ If Not dictCategories.Exists(catName) Then
+ Set cat = New clsCategory
+ cat.categoryName = catName
+ cat.ParentCategoryName = parentCatName
+ cat.CategorySelectCondition = catSelectCond ' ⭐ 新增
+ Set dictCategories(catName) = cat
+ End If
+
+ ' 将物料添加到类别
+ dictCategories(catName).AddMaterial mat
+ End If
+ Next i
+
+ ' 建立类别层级关系(逻辑不变)
+ ' ...
+End Sub
+```
+
+### B. 新增 GetRequiredCategories 方法(伪代码)
+
+```vba
+Private Function GetRequiredCategories(conditions As Object) As collection
+ Dim result As New collection
+ Dim matcher As New clsConditionMatcher
+
+ ' 遍历所有类别
+ Dim key As Variant
+ For Each key In dictCategories.Keys
+ Dim cat As clsCategory
+ Set cat = dictCategories(key)
+
+ ' 检查该类别是否需要
+ If cat.IsRequiredForModel(conditions) Then
+ result.Add cat
+ End If
+ Next key
+
+ Set GetRequiredCategories = result
+End Function
+```
+
+### C. 新增 clsCategory.IsRequiredForModel 方法(伪代码)
+
+```vba
+Public Function IsRequiredForModel(conditions As Object) As Boolean
+ If Me.CategorySelectCondition = "" Then
+ ' 选用条件为空,总是需要
+ IsRequiredForModel = True
+ Else
+ ' 需要用 conditions 去匹配 CategorySelectCondition
+ Dim matcher As New clsConditionMatcher
+ IsRequiredForModel = matcher.IsMatch(Me.CategorySelectCondition, conditions)
+ End If
+End Function
+```
+
+---
+
+**文档版本**:v1.0
+**创建日期**:2026-01-29
+**最后更新**:2026-01-29