# 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代码字段 最终形成的数据结构支持: - 快速按代号查找物料 - 快速按类别查找物料 - 遍历完整的类别树 - 根据型号动态确定需要的类别 - 支持父类别/子类别的领料逻辑