docs: expand AutoBOM architecture and implementation details

Refine project overview to include specific business context (Blaidy Company) and the main workbook file. Restructure the architecture section into a layered object-oriented design (Data, Processing, Application layers) and define the responsibilities of core VBA classes.

Add new technical documentation sections covering:
- Model number format parsing structure
- Condition expression language syntax
- Category hierarchy logic and picking strategies
- Material matching pipeline flow

Include VBA code examples for key methods such as CollectAllMaterials and GetMaterialsByCategoryAndModel to illustrate recursive traversal and material retrieval logic. Clarify the structure of key Excel configuration sheets.
This commit is contained in:
Misaka_Company
2026-01-20 13:42:03 +08:00
parent bc5ea92c52
commit a1347017e3
13 changed files with 2682 additions and 828 deletions

165
CLAUDE.md
View File

@@ -4,84 +4,135 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
## Project Overview
AutoBOM is an Excel VBA-based Bill of Materials (BOM) Management System for manufacturing environments. It manages material hierarchies and procurement logic, supporting both assembled components and individual parts sourcing strategies.
**AutoBOM** is an automated Bill of Materials (BOM) management system for industrial pressure gauge products at 布莱迪公司 (Blaidy Company). The system parses complex product model numbers, extracts material requirements based on specifications, and generates picking lists for manufacturing.
The main Excel file is `YTHN-100.A0.532-BOM-1.2版 Claude.xlsm` (macro-enabled workbook), which contains both the VBA code and the business data.
## Architecture
### Core Classes (VBA/ClassModules/)
### Core Components (Layered Object-Oriented Design)
- **clsBOMManager** - Central controller managing the entire BOM data structure
- Loads data from two Excel worksheets: "领料配置" (Configuration) and "平台配置清单" (Platform Configuration)
- Builds hierarchical category trees with parent-child relationships
- Provides material querying interfaces
- Key methods: `LoadData()`, `GetMaterialsForPicking()`, `GetCategory()`
**Data Layer** (Class Modules)
- `clsMaterialItem` - Individual material entity
- `clsCategory` - Hierarchical category container with parent-child relationships
- `clsBOMManager` - Central controller managing the entire BOM data structure
- **clsCategory** - Represents material categories with hierarchical relationships
- Contains collections of materials and sub-categories
- Tracks parent category for tree navigation
- Properties: `categoryName`, `ParentCategoryName`, `Materials`, `SubCategories`, `IsLeafCategory`
**Processing Layer** (Class Modules)
- `clsModelParser` - Parses complex product model strings into structured components
- `clsConditionExtractor` - Extracts selection conditions from parsed models
- `clsConditionMatcher` - Evaluates logical expressions for material matching
- **clsMaterialItem** - Represents individual material items
- Properties: `code` (物料代号), `Name` (物料名称), `Quantity`, `Condition` (选择条件), `Category`, `ParentCategory`
**Application Layer** (Standard Modules)
- `modBOMProcessor` - BOM operations and usage examples
- `modModelParserTest` - Comprehensive test suite (6 test functions)
- `modModelParserExamples` - Real-world usage examples
### Data Structure Design
### Model Number Format
The system uses a dual-layer approach:
1. **dictAllMaterials** - Complete material database loaded from platform configuration sheet
2. **dictCategories** - Hierarchical category structure with materials filtered by configuration sheet
Product models follow this structure:
```
[Model]-[Diameter].[Install].[Shell].[Connection].[Range].[Char]|[Dial]|[Accessory]|[Flange]
```
Only materials appearing in the "领料配置" worksheet are marked for procurement. Materials in the platform database but not in configuration are ignored.
Example: `YTHN-100.A0.532.M203.M16.Y3|BP-095.2312.M16.PA3`
### Business Logic
The parser uses recursive descent parsing with structure validation.
**Dual-Sourcing Strategy** (via `GetMaterialsForPicking(categoryName, useParent)`):
### Condition Expression Language
- **Parent Mode** (`useParent=True`): Retrieves parent category materials (assembled components)
- Default procurement method
- Example: "低压接头部件" (Low-pressure connector assembly)
Materials are selected using logical expressions with variables:
- `gclj` (过程连接 - process connection)
- `jycz` (接液材质 - liquid contact material)
- `lcfw` (量程范围 - range)
- **Child Mode** (`useParent=False`): Retrieves child category materials (individual parts)
- Used when parent materials are insufficient
- Recursively expands all sub-categories
- Example: "径向低压接头" + "弹簧管" + "螺旋管" (individual components)
Supported operators: `AND`, `OR`, `!=`, parentheses
## Running the Code
Example: `lcfw=M16 AND gclj!=M20 OR (lcfw=M02 AND gclj=Z12)`
1. Open `YTHN-100.A0.532-BOM-1.2版 Claude.xlsm` in Microsoft Excel
2. Enable macros when prompted
3. Press Alt+F8 to open the Macro dialog
4. Run procedures from modBOMProcessor.bas:
- `TestBOMStructure` - Test data loading and verify structure
- `GeneratePickingList` - Generate procurement lists to new worksheet
- `GetCategoryMaterials` - Demo material retrieval logic
- `QueryMaterialInfo` - Query specific category information
### Category Hierarchy Logic
## Data Worksheet Format
The system supports two picking strategies:
1. **Parent Category Mode** (default): Pick assembled components
2. **Child Category Mode** (fallback): Pick individual parts for assembly
**领料配置** (Configuration):
- Column A: 物料代号 (Material Code)
- Column C: 类别 (Category)
- Column D: 上层类别 (Parent Category)
- Row 1: Header, data starts from row 2
Recursive tree traversal is used for material collection through the category hierarchy.
**平台配置清单** (Platform Configuration):
- Column C: 代号 (Code)
- Column D: 名称 (Name)
- Column E: 数量 (Quantity)
- Column F: 选择条件 (Selection Condition)
- Rows 1-3: Headers, data starts from row 4
### Material Matching Pipeline
## VBA Metadata
```
Model String → Parser → Extractor → Conditions Dictionary → Matcher → Filtered Materials
```
The VBA module structure is tracked in `VBA/vba_metadata.json`. This file is automatically maintained and should not be manually edited.
## Data Sheets Structure
## Code Style
The Excel workbook contains two key sheets:
- All comments are in Chinese, explaining business logic and data structures
- Extensive inline documentation for complex logic
- Dictionary objects used for O(1) lookups by category name and material code
- Collection objects used for ordered iteration of materials and categories
**领料配置** (Picking Configuration)
- Defines category hierarchy
- Specifies which materials require picking
- Columns: 物料代号, 类别名称, 上层类别名称
## Version Control
**平台配置清单** (Platform Configuration)
- Complete material database
- Rows 1-3: Headers
- Row 4+: Data
The `.gitignore` excludes `.xlsm` files from version control. Only VBA source code and metadata files are tracked in Git.
## Testing
Run tests from the VBA editor or Excel macros:
- **All tests**: `RunAllTests()` in `modModelParserTest`
- **Individual tests**: Test1-Test6 functions in `modModelParserTest`
- Test1: Model parser basic functionality
- Test2: Condition extractor
- Test3: Condition matcher (14 test cases)
- Test4: Full workflow test
- Test5: All categories matching
- Test6: Extraction rules display
## Development Workflow
1. Edit VBA code in Excel VBA Editor (Alt+F11)
2. Export modules to `VBA/` directory
3. Update `VBA/vba_metadata.json` when adding/removing modules
4. Commit changes to git
**Note**: The main Excel workbook (*.xlsm) is excluded from git by .gitignore.
## Code Conventions
**Naming**
- Classes: `cls` prefix (e.g., `clsBOMManager`)
- Modules: `mod` prefix (e.g., `modBOMProcessor`)
- Public properties: PascalCase
- Local variables: camelCase
**Comments**
- Section headers: `' ========================================`
- Chinese comments for business logic (domain-specific)
- English comments for technical implementation
- Detailed parameter/return value documentation
**Error Handling**
- `On Error GoTo ErrorHandler` pattern
- Conservative: return `Nothing` or `False` on error
## Key Business Logic
- Materials have "selection conditions" expressed as logical formulas
- Not all materials in platform config require picking (filtered by "领料配置" sheet)
- Supports "部件" (component) vs "零件" (part) picking strategies
- Condition extraction is data-driven (new variables can be added)
- Category tree can have arbitrary depth
## Documentation
- `布莱迪公司压力表产品选型大表.md` - Complete product model numbering system (716 lines, Chinese)
- `VBA/clsBOMManager使用说明.md` - API documentation and usage examples for clsBOMManager (546 lines, Chinese)
## Module Registry
`VBA/vba_metadata.json` maps VBA module names to their file locations in the repository. Update this file when:
- Adding new class modules or standard modules
- Renaming existing modules
- Reorganizing the directory structure

View File

@@ -0,0 +1,464 @@
# Example1_GeneratePickingListByModel 过程时序图
## 过程说明
本时序图展示 `Example1_GeneratePickingListByModel` 过程的详细执行流程,该过程根据产品型号生成完整的领料清单。
---
## 1. 简略版时序图(概览)
此版本展示主要流程和关键交互,适合快速了解整体架构。
```mermaid
sequenceDiagram
autonumber
participant Main as Example1_Sub
participant BOMMgr as clsBOMManager
participant Excel as Excel工作表
participant PickList as 型号领料清单表
participant Parser as 型号解析组件
participant DataStore as 数据集合
%% ==================== 初始化 ====================
Note over Main: 1. 初始化与加载数据
Main->>BOMMgr: 创建并初始化 BOMManager
Main->>Excel: 获取配置工作表<br/>(领料配置、平台配置清单)
Main->>BOMMgr: LoadData(wsConfig, wsPlatform)
activate BOMMgr
BOMMgr->>Excel: 读取类别层级和物料数据
BOMMgr->>DataStore: 构建内存数据结构<br/>(dictCategories, dictAllMaterials)
deactivate BOMMgr
%% ==================== 创建输出表 ====================
Note over Main: 2. 创建输出工作表
Main->>Main: modelStr = "YTHN-100.A0.532.M203.M16.Y3|..."
Main->>Excel: 删除旧工作表(如存在)
Main->>Excel: 创建新工作表 "型号领料清单"
Excel-->>Main: PickList 工作表对象
%% ==================== 解析型号 ====================
Note over Main: 3. 解析型号并提取条件
Main->>PickList: 写入产品型号标题
Main->>Parser: ParseModelAndExtractConditions(modelStr)
activate Parser
Parser->>Parser: 解析表头型号(YTHN-100...)<br/>解析表盘型号(BP-095...)
Parser->>DataStore: 提取选择条件<br/>(gclj, jycz, lcfw)
Parser-->>Main: Conditions Dictionary
deactivate Parser
Main->>PickList: 写入提取条件到工作表
%% ==================== 写入表头 ====================
Note over Main: 4. 写入数据表头
Main->>PickList: 写入列标题<br/>(类别, 代号, 名称, 数量, 选择条件, 匹配状态)
%% ==================== 处理物料 ====================
Note over Main: 5. 遍历类别并匹配物料
Main->>BOMMgr: GetRootCategories()
BOMMgr-->>Main: 根类别集合
loop 每个根类别
Main->>BOMMgr: GetMaterialsByModel(modelStr, categoryName)
activate BOMMgr
BOMMgr->>DataStore: 获取类别对象
BOMMgr->>Parser: 匹配物料选择条件
Parser-->>BOMMgr: 匹配结果集合
BOMMgr-->>Main: Materials Collection
deactivate BOMMgr
Note over Main: 写入该类别的所有物料
loop 每个匹配的物料
Main->>PickList: 写入物料详细信息<br/>(代号, 名称, 数量, 条件)
end
end
%% ==================== 完成输出 ====================
Note over Main: 6. 格式化并完成
Main->>PickList: AutoFit 格式化
Main->>Main: 显示完成消息框
```
**简略版特点**
- **6个参与者**相比详细版的11个
- **6个主要阶段**相比详细版的14个步骤
- **合并了内部细节**:解析器、提取器、匹配器的内部操作合并为一个组件
- **简化循环展示**:不展示条件判断、分支逻辑等细节
- **适合**:快速理解整体流程和架构
---
## 2. 详细版时序图(完整流程)
此版本展示每个方法的调用细节、内部逻辑和数据流向,适合深入理解实现。
## 时序图
```mermaid
sequenceDiagram
autonumber
participant Main as Example1_Sub
participant BOMMgr as clsBOMManager
participant Workbooks as Excel.Worksheets
participant ConfigWS as 领料配置表
participant PlatformWS as 平台配置清单表
participant Parser as clsModelParser
participant Extractor as clsConditionExtractor
participant Matcher as clsConditionMatcher
participant PickListWS as 型号领料清单表
participant RootCats as 根类别Collection
participant Cat as clsCategory
participant Materials as 物料Collection
participant Conditions as 条件Dictionary
%% ==================== 初始化阶段 ====================
Note over Main: 1. 初始化 BOM 管理器
Main->>BOMMgr: New clsBOMManager
activate BOMMgr
BOMMgr->>BOMMgr: 初始化 dictCategories<br/>初始化 dictAllMaterials<br/>初始化 rootCategories
deactivate BOMMgr
%% ==================== 加载配置阶段 ====================
Note over Main: 2. 获取工作表对象
Main->>Workbooks: Worksheets("领料配置")
Workbooks-->>Main: ConfigWS
Main->>Workbooks: Worksheets("平台配置清单")
Workbooks-->>Main: PlatformWS
Note over Main: 3. 加载 BOM 数据
Main->>BOMMgr: LoadData(ConfigWS, PlatformWS)
activate BOMMgr
BOMMgr->>ConfigWS: 读取类别配置数据<br/>第2行起物料代号、类别名称、上层类别
ConfigWS-->>BOMMgr: 类别层级数据
BOMMgr->>BOMMgr: 构建类别树结构<br/>创建 clsCategory 对象<br/>建立父子关系
BOMMgr->>PlatformWS: 读取物料清单数据<br/>第4行起
PlatformWS-->>BOMMgr: 完整物料数据
BOMMgr->>BOMMgr: 创建 clsMaterialItem 对象<br/>填充 dictAllMaterials 字典
BOMMgr->>BOMMgr: 将物料分配到对应类别<br/>建立 rootCategories 集合
deactivate BOMMgr
%% ==================== 准备型号数据 ====================
Note over Main: 4. 定义产品型号
Main->>Main: modelStr = "YTHN-100.A0.532.M203.M16.Y3|BP-095.2312.M16.PA3"
%% ==================== 创建输出工作表 ====================
Note over Main: 5. 删除旧的领料清单表
Main->>Workbooks: Worksheets("型号领料清单")
alt 工作表存在
Workbooks-->>Main: 工作表对象
Main->>PickListWS: Delete
else 工作表不存在
Workbooks-->>Main: 错误
Main->>Main: On Error Resume Next (忽略错误)
end
Note over Main: 6. 创建新工作表
Main->>Workbooks: Worksheets.Add()
Workbooks-->>Main: PickListWS
Main->>PickListWS: Name = "型号领料清单"
%% ==================== 写入标题行 ====================
Note over Main: 7. 写入产品型号
Main->>Main: row = 1
Main->>PickListWS: Cells(row, 1).value = "产品型号"
Main->>PickListWS: Cells(row, 2).value = modelStr
Main->>Main: row = row + 1
%% ==================== 解析型号并提取条件 ====================
Note over Main: 8. 解析型号提取条件
Main->>BOMMgr: ParseModelAndExtractConditions(modelStr)
activate BOMMgr
BOMMgr->>Parser: New clsModelParser
activate Parser
BOMMgr->>Parser: ParseModel(modelStr)
Note over Parser: 解析表头型号
Parser->>Parser: 提取型号前缀: "YTHN"
Parser->>Parser: 提取公称外径: "100"
Parser->>Parser: 提取安装形式: "A0"
Parser->>Parser: 提取壳体形式: "532"
Parser->>Parser: 提取过程连接: "M203"
Parser->>Parser: 提取量程范围: "M16"
Parser->>Parser: 提取仪表特性: "Y3"
Note over Parser: 解析表盘型号
Parser->>Parser: 提取表盘型号: "BP-095"
Parser->>Parser: 提取表盘规格: "2312"
Parser->>Parser: 提取表盘连接: "M16"
Parser->>Parser: 提取表盘特性: "PA3"
Parser-->>BOMMgr: True (解析成功)
deactivate Parser
BOMMgr->>Extractor: New clsConditionExtractor
activate Extractor
BOMMgr->>Extractor: ExtractConditions(Parser)
Note over Extractor: 应用提取规则
Extractor->>Extractor: 规则1: gclj = 过程连接 = "M203"
Extractor->>Extractor: 规则2: jycz = 接液材质 = "M16"
Extractor->>Extractor: 规则3: lcfw = 量程范围 = "M16"
Extractor->>Extractor: 创建条件字典
Extractor-->>BOMMgr: Conditions Dictionary
deactivate Extractor
BOMMgr-->>Main: Conditions Dictionary
deactivate BOMMgr
%% ==================== 写入提取条件 ====================
Note over Main: 9. 写入提取条件行
Main->>PickListWS: Cells(row, 1).value = "提取条件"
Note over Main: 遍历条件字典拼接字符串
Main->>Conditions: 遍历 Keys
loop 每个条件键值对
Conditions-->>Main: key, value
Main->>Main: 拼接条件字符串到 condStr
end
Main->>PickListWS: Cells(row, 2).value = condStr
Main->>Main: row = row + 2
%% ==================== 写入表头 ====================
Note over Main: 10. 写入数据表头
Main->>PickListWS: Cells(row, 1).value = "类别"
Main->>PickListWS: Cells(row, 2).value = "代号"
Main->>PickListWS: Cells(row, 3).value = "名称"
Main->>PickListWS: Cells(row, 4).value = "数量"
Main->>PickListWS: Cells(row, 5).value = "选择条件"
Main->>PickListWS: Cells(row, 6).value = "匹配状态"
Main->>PickListWS: 设置表头字体为粗体 (Range A-F)
Main->>Main: row = row + 1
%% ==================== 获取根类别 ====================
Note over Main: 11. 获取根类别集合
Main->>BOMMgr: GetRootCategories()
BOMMgr-->>Main: RootCats Collection
%% ==================== 外层循环:遍历根类别 ====================
Note over Main: 12. 遍历每个根类别
loop 每个根类别 i = 1 To RootCats.Count
Main->>RootCats: Item(i)
RootCats-->>Main: Cat
Note over Main: 获取类别名称
Main->>Cat: .categoryName
Cat-->>Main: categoryName
Note over Main: 获取该类别符合条件的物料
Main->>BOMMgr: GetMaterialsByModel(modelStr, categoryName)
activate BOMMgr
BOMMgr->>Matcher: New clsConditionMatcher
activate Matcher
Note over BOMMgr: 从 dictCategories 获取类别
BOMMgr->>BOMMgr: dictCategories(categoryName)
BOMMgr-->>BOMMgr: Category 对象
Note over BOMMgr: 遍历类别下的所有物料
BOMMgr->>Cat: .materials (Collection)
loop 每个物料
Cat-->>BOMMgr: MaterialItem
Note over BOMMgr: 检查物料选择条件
BOMMgr->>MaterialItem: .Condition
alt 物料有选择条件
MaterialItem-->>BOMMgr: conditionStr
Note over BOMMgr,Matcher: 匹配条件
BOMMgr->>Matcher: Match(conditionStr, Conditions)
Matcher->>Matcher: 解析条件表达式<br/>AND, OR, !=, 括号)
Matcher->>Conditions: 获取变量值
Conditions-->>Matcher: 变量值
Matcher->>Matcher: 评估逻辑表达式
Matcher-->>BOMMgr: True/False (匹配结果)
alt 匹配成功
BOMMgr->>BOMMgr: 添加到结果集合
end
else 物料无条件
MaterialItem-->>BOMMgr: ""
BOMMgr->>BOMMgr: 直接添加到结果集合
end
end
BOMMgr-->>Main: Materials Collection
deactivate Matcher
deactivate BOMMgr
%% ==================== 内层循环:遍历物料写入 ====================
Note over Main: 写入该类别的所有物料
loop 每个物料 j = 1 To Materials.Count
Main->>Materials: Item(j)
Materials-->>Main: mat (clsMaterialItem)
Note over Main: 写入物料详细信息
Main->>PickListWS: Cells(row, 1).value = categoryName
Main->>PickListWS: Cells(row, 2).value = mat.code
Main->>PickListWS: Cells(row, 3).value = mat.Name
Main->>PickListWS: Cells(row, 4).value = mat.Quantity
Main->>mat: .Condition
mat-->>Main: condition
alt condition = ""
Main->>PickListWS: Cells(row, 5).value = "(无条件)"
else condition <> ""
Main->>PickListWS: Cells(row, 5).value = condition
end
Main->>PickListWS: Cells(row, 6).value = "✓"
Main->>Main: row = row + 1
end
end
%% ==================== 格式化输出 ====================
Note over Main: 13. 格式化工作表
Main->>PickListWS: Columns("A:F").AutoFit
%% ==================== 显示完成消息 ====================
Note over Main: 14. 显示完成消息框
Main->>Main: MsgBox("领料清单生成完成!\n请查看工作表: 型号领料清单")
Note over Main: 过程结束
```
## 流程阶段说明
### 阶段 1: 初始化与数据加载 (步骤 1-3)
- 创建 BOM 管理器实例
- 从 Excel 获取配置工作表
- 加载类别层级和物料数据,构建内存数据结构
### 阶段 2: 工作表准备 (步骤 4-7)
- 定义产品型号字符串
- 删除旧的输出工作表(带错误处理)
- 创建新的"型号领料清单"工作表
- 写入产品型号标题
### 阶段 3: 型号解析与条件提取 (步骤 8-9)
- 使用 ModelParser 解析复杂型号字符串
- 表头型号YTHN-100.A0.532.M203.M16.Y3
- 表盘型号BP-095.2312.M16.PA3
- 使用 ConditionExtractor 提取物料选择条件
- 将条件字典写入输出表
### 阶段 4: 数据表头 (步骤 10-11)
- 写入数据列标题:类别、代号、名称、数量、选择条件、匹配状态
- 获取根类别集合
### 阶段 5: 物料匹配与写入 (步骤 12)
- **外层循环**:遍历每个根类别
- **物料匹配**:对每个类别调用 GetMaterialsByModel
- 遍历类别下所有物料
- 使用 ConditionMatcher 评估每个物料的选择条件
- 筛选符合条件的物料
- **内层循环**:遍历匹配的物料集合
- 写入物料的详细信息(代号、名称、数量等)
- 显示选择条件和匹配状态
### 阶段 6: 格式化与完成 (步骤 13-14)
- 自动调整列宽
- 显示完成消息框
## 关键数据结构
### 输入数据
- **产品型号**: "YTHN-100.A0.532.M203.M16.Y3|BP-095.2312.M16.PA3"
- **配置工作表**: "领料配置"、"平台配置清单"
### 中间数据
- **Conditions Dictionary**: {gclj: "M203", jycz: "M16", lcfw: "M16"}
- **RootCategories Collection**: 所有顶层类别
- **Materials Collection**: 每个类别匹配的物料
### 输出数据
- **型号领料清单表**: 包含产品型号、提取条件、物料清单
## 性能考虑
- 使用 Dictionary 和 Collection 提高查找效率
- 嵌套循环结构:根类别 × 物料
- 大量 Excel 单元格写入操作(可考虑批量写入优化)
## 扩展建议
1. **批量写入优化**: 将数据先存入数组,一次性写入工作表
2. **进度显示**: 添加处理进度条
3. **错误处理**: 增强型号解析失败、数据缺失等异常处理
4. **参数化**: 将型号作为参数传入,增加灵活性
5. **日志记录**: 记录处理过程和匹配统计信息
---
## 3. 两个版本的对比
### 复杂度对比
| 特性 | 简略版 | 详细版 |
|------|--------|--------|
| **参与者数量** | 6个 | 11个 |
| **主要阶段** | 6个 | 14个步骤 |
| **交互步骤** | 约35步 | 约80步 |
| **循环展开** | 简化展示 | 完整展示内部逻辑 |
| **条件分支** | 隐藏 | 详细展示 (alt/opt/loop) |
| **内部方法调用** | 合并展示 | 逐个展开 |
### 适用场景
#### 简略版适用于:
- 📌 **初次了解**:快速掌握整体流程和架构
- 📌 **高层汇报**:向非技术人员展示系统工作原理
- 📌 **文档概览**:作为技术文档的目录或概述部分
- 📌 **快速参考**:复习主要步骤和组件关系
#### 详细版适用于:
- 🔍 **代码调试**:理解具体实现细节和数据流向
- 🔍 **代码审查**:检查逻辑错误和性能瓶颈
- 🔍 **新人培训**:深入讲解系统实现机制
- 🔍 **维护文档**:作为详细技术文档保存
### 组件对应关系
| 简略版组件 | 详细版组件 |
|------------|------------|
| Excel工作表 | Workbooks + ConfigWS + PlatformWS + PickListWS |
| 型号解析组件 | Parser + Extractor + Matcher |
| 数据集合 | RootCats + Cat + Materials + Conditions |
### 阶段对应关系
| 简略版阶段 | 详细版步骤 |
|------------|------------|
| 1. 初始化与加载数据 | 1-3: 初始化、获取工作表、加载数据 |
| 2. 创建输出工作表 | 4-7: 定义型号、删除旧表、创建新表、写入标题 |
| 3. 解析型号并提取条件 | 8-9: 解析型号、提取条件、写入条件行 |
| 4. 写入数据表头 | 10-11: 写入列标题、获取根类别 |
| 5. 遍历类别并匹配物料 | 12: 外层循环(类别)、内层循环(物料)、条件匹配 |
| 6. 格式化并完成 | 13-14: 格式化工作表、显示消息框 |
---
## 使用建议
1. **学习路径**:先看简略版建立整体概念,再看详细版理解实现细节
2. **文档组织**:简略版放在文档开头作为概述,详细版放在后面作为参考
3. **演示场景**简略版适合PPT演示详细版适合技术讲解
4. **版本维护**:代码变更时,优先更新详细版,然后同步更新简略版的主要流程

View File

@@ -16,7 +16,7 @@ Private dictCategories As Object ' Dictionary对象: 类别名称 -> clsCateg
' 作用: 快速查找任意类别
Private dictAllMaterials As Object ' Dictionary对象: 物料代号 -> clsMaterialItem对象
' 作用: 快速查找任意物料
Private rootCategories As Collection ' Collection: 存储所有顶层类别(无父类别的类别)
Private rootCategories As collection ' Collection: 存储所有顶层类别(无父类别的类别)
' 作用: 遍历完整的类别树结构
' ========================================
@@ -26,7 +26,7 @@ Private rootCategories As Collection ' Collection: 存储所有顶层类别(无
Private Sub Class_Initialize()
Set dictCategories = CreateObject("Scripting.Dictionary")
Set dictAllMaterials = CreateObject("Scripting.Dictionary")
Set rootCategories = New Collection
Set rootCategories = New collection
End Sub
' ========================================
@@ -59,12 +59,12 @@ Public Sub LoadData(wsConfig As Worksheet, wsPlatform As Worksheet)
lastRow = wsPlatform.Cells(wsPlatform.Rows.Count, "C").End(xlUp).row
For i = 4 To lastRow ' 4()
Set mat = New clsMaterialItem
mat.code = Trim(wsPlatform.Cells(i, "C").Value & "") ' 物料代号
mat.Name = Trim(wsPlatform.Cells(i, "D").Value & "") ' 物料名称
mat.code = Trim(wsPlatform.Cells(i, "C").value & "") ' 物料代号
mat.Name = Trim(wsPlatform.Cells(i, "D").value & "") ' 物料名称
On Error Resume Next
mat.Quantity = CDbl(wsPlatform.Cells(i, "E").Value) '
mat.Quantity = CDbl(wsPlatform.Cells(i, "E").value) '
On Error GoTo 0
mat.Condition = Trim(wsPlatform.Cells(i, "F").Value & "") ' 选择条件
mat.Condition = Trim(wsPlatform.Cells(i, "F").value & "") ' 选择条件
'
If mat.code <> "" Then
@@ -86,9 +86,9 @@ Public Sub LoadData(wsConfig As Worksheet, wsPlatform As Worksheet)
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 & "") ' 上层类别名称
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
@@ -155,7 +155,7 @@ End Sub
' Debug.Print cats(i).CategoryName
' Next i
' ========================================
Public Function GetRootCategories() As Collection
Public Function GetRootCategories() As collection
Set GetRootCategories = rootCategories
End Function
@@ -208,9 +208,9 @@ End Function
' ' 返回: 径向低压接头、弹簧管、螺旋管等零件
' ========================================
Public Function GetMaterialsForPicking(categoryName As String, _
Optional useParent As Boolean = True) As Collection
Dim result As Collection
Set result = New Collection
Optional useParent As Boolean = True) As collection
Dim result As collection
Set result = New collection
' 查找指定类别
Dim cat As clsCategory
@@ -232,8 +232,8 @@ Public Function GetMaterialsForPicking(categoryName As String, _
' - 这是正常情况下的领料方式(领取组装好的部件)
' ========================================
Dim m As clsMaterialItem
For i = 1 To cat.Materials.Count
Set m = cat.Materials(i)
For i = 1 To cat.materials.Count
Set m = cat.materials(i)
result.Add m
Next i
Else
@@ -253,7 +253,7 @@ Public Function GetMaterialsForPicking(categoryName As String, _
Dim j As Long
For j = 1 To cat.SubCategories.Count
Set subCat = cat.SubCategories(j)
Dim subMats As Collection
Dim subMats As collection
' 递归调用,继续展开子类别
Set subMats = GetMaterialsForPicking(subCat.categoryName, False)
Dim k As Long
@@ -263,8 +263,8 @@ Public Function GetMaterialsForPicking(categoryName As String, _
Next j
Else
' 叶子类别,返回本类别物料
For i = 1 To cat.Materials.Count
Set m = cat.Materials(i)
For i = 1 To cat.materials.Count
Set m = cat.materials(i)
result.Add m
Next i
End If
@@ -297,7 +297,7 @@ End Function
Public Sub PrintCategoryTree(ws As Worksheet)
Dim row As Long
row = 1
ws.Cells(row, 1).Value = "类别层级结构"
ws.Cells(row, 1).value = "类别层级结构"
row = row + 1
' 遍历所有根类别,递归打印整个树
@@ -329,19 +329,19 @@ Private Sub PrintCategory(ws As Worksheet, cat As clsCategory, _
indent = String(level * 2, " ")
'
ws.Cells(row, 1).Value = indent & cat.categoryName & _
" (物料数:" & cat.Materials.Count & ")"
ws.Cells(row, 1).value = indent & cat.categoryName & _
" (物料数:" & cat.materials.Count & ")"
row = row + 1
' 打印该类别下的所有物料
Dim mat As clsMaterialItem
Dim i As Long
For i = 1 To cat.Materials.Count
Set mat = cat.Materials(i)
For i = 1 To cat.materials.Count
Set mat = cat.materials(i)
' 2,"- "
ws.Cells(row, 1).Value = indent & " - " & mat.code & " " & mat.Name
ws.Cells(row, 2).Value = "数量:" & mat.Quantity
ws.Cells(row, 3).Value = "条件:" & mat.Condition
ws.Cells(row, 1).value = indent & " - " & mat.code & " " & mat.Name
ws.Cells(row, 2).value = "数量:" & mat.Quantity
ws.Cells(row, 3).value = "条件:" & mat.Condition
row = row + 1
Next i
@@ -354,3 +354,180 @@ Private Sub PrintCategory(ws As Worksheet, cat As clsCategory, _
Call PrintCategory(ws, subCat, row, level + 1)
Next j
End Sub
' ========================================
' GetMaterialsByModel 方法
' 功能: 根据产品型号获取符合条件的物料清单
' 参数:
' modelStr - 产品型号字符串
' categoryName - 可选,指定类别名称。为空则返回所有类别的物料
' 返回: Collection对象包含符合条件的clsMaterialItem对象
' 示例:
' Set materials = bomMgr.GetMaterialsByModel("YTHN-100.A0.532.M203.M16.Y3")
' Set materials = bomMgr.GetMaterialsByModel("YTHN-100.A0.532.M203.M16.Y3", "部件")
' ========================================
Public Function GetMaterialsByModel(modelStr As String, _
Optional categoryName As String = "") As collection
Dim result As collection
Set result = New collection
' 1.
Dim parser As New clsModelParser
If Not parser.ParseModel(modelStr) Then
Debug.Print "型号解析失败: " & parser.ErrorMessage
Set GetMaterialsByModel = result
Exit Function
End If
' 2. 提取条件
Dim extractor As New clsConditionExtractor
Dim conditions As Object
Set conditions = extractor.ExtractConditions(parser)
'
Debug.Print "【型号】: " & modelStr
Debug.Print "【提取的条件】:"
Dim key As Variant
For Each key In conditions.Keys
Debug.Print " " & key & " = " & conditions(key)
Next key
Debug.Print ""
' 3. 创建条件匹配器
Dim matcher As New clsConditionMatcher
' 4.
Dim materialsToFilter As collection
Set materialsToFilter = New collection
If categoryName <> "" Then
' 指定类别:获取该类别的所有物料
Dim cat As clsCategory
Set cat = GetCategory(categoryName)
If Not cat Is Nothing Then
Dim i As Long
For i = 1 To cat.materials.Count
materialsToFilter.Add cat.materials(i)
Next i
End If
Else
' 未指定类别:获取所有根类别的物料
Dim rootCat As clsCategory
Dim j As Long
For j = 1 To rootCategories.Count
Set rootCat = rootCategories(j)
' 递归获取所有物料
Call CollectAllMaterials(rootCat, materialsToFilter)
Next j
End If
' 5. 筛选符合条件的物料
Dim mat As clsMaterialItem
Dim matchCount As Long
matchCount = 0
For Each mat In materialsToFilter
' 使用条件匹配器判断
If matcher.IsMatch(mat.Condition, conditions) Then
result.Add mat
matchCount = matchCount + 1
'
Debug.Print "【匹配】 " & mat.code & " - " & mat.Name & _
" | 条件: " & IIf(mat.Condition = "", "(无)", mat.Condition)
End If
Next mat
Debug.Print "共匹配 " & matchCount & " 个物料"
Debug.Print String(60, "=")
Set GetMaterialsByModel = result
End Function
' ========================================
' CollectAllMaterials 方法 (私有)
' 功能: 递归收集类别及其所有子类别的物料
' 参数:
' cat - 类别对象
' collection - 用于存储物料的Collection
' ========================================
Private Sub CollectAllMaterials(cat As clsCategory, collection As collection)
Dim i As Long
' 添加当前类别的所有物料
For i = 1 To cat.materials.Count
collection.Add cat.materials(i)
Next i
' 递归处理子类别
Dim subCat As clsCategory
Dim j As Long
For j = 1 To cat.SubCategories.Count
Set subCat = cat.SubCategories(j)
Call CollectAllMaterials(subCat, collection)
Next j
End Sub
' ========================================
' GetMaterialsByCategoryAndModel 方法
' 功能: 根据类别和型号获取物料(使用父类别或子类别逻辑)
' 参数:
' modelStr - 产品型号字符串
' categoryName - 类别名称
' useParent - True=使用父类别物料False=使用子类别物料
' 返回: Collection对象包含符合条件的clsMaterialItem对象
' 说明: 这是 GetMaterialsForPicking 和 GetMaterialsByModel 的结合
' ========================================
Public Function GetMaterialsByCategoryAndModel(modelStr As String, _
categoryName As String, _
Optional useParent As Boolean = True) As collection
Dim result As collection
Set result = New collection
' 1. 解析型号并提取条件
Dim parser As New clsModelParser
If Not parser.ParseModel(modelStr) Then
Set GetMaterialsByCategoryAndModel = result
Exit Function
End If
Dim extractor As New clsConditionExtractor
Dim conditions As Object
Set conditions = extractor.ExtractConditions(parser)
' 2. 获取类别的物料(根据 useParent 参数)
Dim materialsToFilter As collection
Set materialsToFilter = GetMaterialsForPicking(categoryName, useParent)
' 3. 筛选符合条件的物料
Dim matcher As New clsConditionMatcher
Dim mat As clsMaterialItem
For Each mat In materialsToFilter
If matcher.IsMatch(mat.Condition, conditions) Then
result.Add mat
End If
Next mat
Set GetMaterialsByCategoryAndModel = result
End Function
' ========================================
' ParseModelAndExtractConditions 方法
' 功能: 解析型号并返回条件字典(工具方法)
' 参数: modelStr - 产品型号字符串
' 返回: Dictionary对象包含提取的条件
' 用途: 供外部调用,用于查看提取的条件
' ========================================
Public Function ParseModelAndExtractConditions(modelStr As String) As Object
Dim parser As New clsModelParser
Dim extractor As New clsConditionExtractor
Dim conditions As Object
If parser.ParseModel(modelStr) Then
Set conditions = extractor.ExtractConditions(parser)
Else
Set conditions = CreateObject("Scripting.Dictionary")
End If
Set ParseModelAndExtractConditions = conditions
End Function

View File

@@ -6,19 +6,19 @@ Option Explicit
Public categoryName As String
Public ParentCategoryName As String
Public Materials As Collection ' 存储 clsMaterialItem 对象
Public SubCategories As Collection ' 存储子类别 clsCategory 对象
Public materials As collection ' 存储 clsMaterialItem 对象
Public SubCategories As collection ' 存储子类别 clsCategory 对象
Public IsLeafCategory As Boolean ' 是否叶子类别(无子类别)
Private Sub Class_Initialize()
Set Materials = New Collection
Set SubCategories = New Collection
Set materials = New collection
Set SubCategories = New collection
IsLeafCategory = True
End Sub
' 添加物料
Public Sub AddMaterial(mat As clsMaterialItem)
Materials.Add mat, mat.code
materials.Add mat, mat.code
End Sub
' 添加子类别
@@ -30,7 +30,7 @@ End Sub
' 获取物料(按代号)
Public Function GetMaterial(code As String) As clsMaterialItem
On Error Resume Next
Set GetMaterial = Materials(code)
Set GetMaterial = materials(code)
On Error GoTo 0
End Function

View File

@@ -0,0 +1,234 @@
' ========================================
' 类模块: clsConditionExtractor
' 用途: 从型号解析器中提取物料选择条件
' ========================================
Option Explicit
' ========================================
' 私有成员变量
' ========================================
Private m_Conditions As Object ' Dictionary: 变量名 -> 条件值
Private m_ExtractionRules As Object ' Dictionary: 提取规则配置
' ========================================
' 类初始化
' ========================================
Private Sub Class_Initialize()
Set m_Conditions = CreateObject("Scripting.Dictionary")
Set m_ExtractionRules = CreateObject("Scripting.Dictionary")
' 初始化提取规则
InitializeRules
End Sub
' ========================================
' InitializeRules 方法 (私有)
' 功能: 初始化条件提取规则
' 说明: 这里配置所有需要提取的条件及其提取方法
' ========================================
Private Sub InitializeRules()
' 规则格式: Dictionary(变量名) = Array(源字段, 提取方法)
' 规则1: 过程连接 (gclj)
' ConnectionCode
m_ExtractionRules("gclj") = Array("ConnectionCode", "RemoveLastDigit")
' 规则2: 接液材质 (jycz)
' ConnectionCode
m_ExtractionRules("jycz") = Array("ConnectionCode", "GetLastDigit")
' 规则3: 量程范围 (lcfw)
' RangeCode
m_ExtractionRules("lcfw") = Array("RangeCode", "Direct")
' 未来可以在这里添加更多提取规则...
' 例如:
' m_ExtractionRules("bplx") = Array("DialCode", "Direct") ' 表盘类型
End Sub
' ========================================
' ExtractConditions 方法
' 功能: 从型号解析器中提取所有条件
' 参数: parser - clsModelParser对象
' 返回: Dictionary对象包含所有提取的条件
' ========================================
Public Function ExtractConditions(parser As clsModelParser) As Object
'
Set m_Conditions = CreateObject("Scripting.Dictionary")
' 验证解析器有效性
If Not parser.IsValid Then
Set ExtractConditions = m_Conditions
Exit Function
End If
' 遍历所有提取规则
Dim varName As Variant
For Each varName In m_ExtractionRules.Keys
Dim ruleInfo As Variant
ruleInfo = m_ExtractionRules(varName)
Dim sourceField As String
Dim extractMethod As String
sourceField = ruleInfo(0)
extractMethod = ruleInfo(1)
' 提取条件值
Dim conditionValue As String
conditionValue = ExtractValue(parser, sourceField, extractMethod)
'
If conditionValue <> "" Then
m_Conditions(CStr(varName)) = conditionValue
End If
Next varName
Set ExtractConditions = m_Conditions
End Function
' ========================================
' ExtractValue 方法 (私有)
' 功能: 根据规则从解析器中提取单个值
' 参数:
' parser - clsModelParser对象
' sourceField - 源字段名称
' extractMethod - 提取方法名称
' 返回: 提取的条件值
' ========================================
Private Function ExtractValue(parser As clsModelParser, _
sourceField As String, _
extractMethod As String) As String
Dim sourceValue As String
'
Select Case sourceField
Case "ConnectionCode"
sourceValue = parser.ConnectionCode
Case "RangeCode"
sourceValue = parser.RangeCode
Case "ModelType"
sourceValue = parser.ModelType
Case "Diameter"
sourceValue = parser.Diameter
Case "InstallForm"
sourceValue = parser.InstallForm
Case "ShellForm"
sourceValue = parser.ShellForm
Case "Characteristics"
sourceValue = parser.Characteristics
Case Else
sourceValue = ""
End Select
'
Select Case extractMethod
Case "Direct"
' 使
ExtractValue = sourceValue
Case "RemoveLastDigit"
'
If Len(sourceValue) > 1 Then
ExtractValue = Left(sourceValue, Len(sourceValue) - 1)
Else
ExtractValue = sourceValue
End If
Case "GetLastDigit"
'
If Len(sourceValue) > 0 Then
ExtractValue = Right(sourceValue, 1)
Else
ExtractValue = ""
End If
Case "GetFirstChar"
'
If Len(sourceValue) > 0 Then
ExtractValue = Left(sourceValue, 1)
Else
ExtractValue = ""
End If
Case Else
'
ExtractValue = ""
End Select
End Function
' ========================================
' GetConditionValue 方法
' 功能: 获取单个条件值
' 参数: varName - 变量名
' 返回: 条件值,如果不存在返回空字符串
' ========================================
Public Function GetConditionValue(varName As String) As String
If m_Conditions.Exists(varName) Then
GetConditionValue = m_Conditions(varName)
Else
GetConditionValue = ""
End If
End Function
' ========================================
' AddCondition 方法
' 功能: 手动添加条件 (用于特殊情况)
' 参数:
' varName - 变量名
' value - 条件值
' ========================================
Public Sub AddCondition(varName As String, value As String)
m_Conditions(varName) = value
End Sub
' ========================================
' GetConditions 属性
' 功能: 获取所有条件的Dictionary对象
' ========================================
Public Property Get conditions() As Object
Set conditions = m_Conditions
End Property
' ========================================
' ToString 方法
' 功能: 返回条件的字符串表示 (用于调试)
' ========================================
Public Function ToString() As String
Dim result As String
result = "【提取的条件】" & vbCrLf
If m_Conditions.Count = 0 Then
result = result & " (无条件)" & vbCrLf
Else
Dim key As Variant
For Each key In m_Conditions.Keys
result = result & " " & key & " = " & m_Conditions(key) & vbCrLf
Next key
End If
ToString = result
End Function
' ========================================
' AddExtractionRule 方法
' 功能: 动态添加新的提取规则 (用于扩展)
' 参数:
' varName - 变量名
' sourceField - 源字段名称
' extractMethod - 提取方法名称
' : extractor.AddExtractionRule "bplx", "DialCode", "Direct"
' ========================================
Public Sub AddExtractionRule(varName As String, _
sourceField As String, _
extractMethod As String)
m_ExtractionRules(varName) = Array(sourceField, extractMethod)
End Sub
' ========================================
' GetExtractionRules 方法
' 功能: 获取当前所有提取规则 (用于调试)
' 返回: Dictionary对象
' ========================================
Public Function GetExtractionRules() As Object
Set GetExtractionRules = m_ExtractionRules
End Function

View File

@@ -0,0 +1,250 @@
' ========================================
' 类模块: clsConditionMatcher
' 用途: 解析物料的选择条件表达式,并判断是否匹配
' 支持: AND, OR, NOT(!=), 括号优先级
' ========================================
Option Explicit
' ========================================
' IsMatch 方法
' 功能: 判断条件表达式是否匹配
' 参数:
' conditionExpr - 条件表达式字符串
' conditions - Dictionary对象包含变量名->值的映射
' 返回: Boolean - 是否匹配
' 示例:
' IsMatch("lcfw=M16 AND gclj=M20", conditions) -> True/False
' ========================================
Public Function IsMatch(conditionExpr As String, conditions As Object) As Boolean
On Error GoTo ErrorHandler
'
If Trim(conditionExpr) = "" Then
IsMatch = True
Exit Function
End If
' 解析并计算表达式
IsMatch = EvaluateExpression(Trim(conditionExpr), conditions)
Exit Function
ErrorHandler:
' False
Debug.Print "条件匹配出错: " & conditionExpr & " - " & Err.description
IsMatch = False
End Function
' ========================================
' EvaluateExpression 方法 (私有)
' 功能: 递归计算逻辑表达式
' 优先级: 括号 > NOT(!=) > AND > OR
' ========================================
Private Function EvaluateExpression(expr As String, conditions As Object) As Boolean
expr = Trim(expr)
' ()
If InStr(expr, "(") > 0 Then
EvaluateExpression = EvaluateWithParentheses(expr, conditions)
Exit Function
End If
' OR ()
If InStr(expr, " OR ") > 0 Then
EvaluateExpression = EvaluateOR(expr, conditions)
Exit Function
End If
' AND ()
If InStr(expr, " AND ") > 0 Then
EvaluateExpression = EvaluateAND(expr, conditions)
Exit Function
End If
' 处理单个条件 (最高优先级)
EvaluateExpression = EvaluateSimpleCondition(expr, conditions)
End Function
' ========================================
' EvaluateWithParentheses 方法 (私有)
' 功能: 处理包含括号的表达式
' 策略: 找到最内层括号,递归计算,然后替换为结果
' ========================================
Private Function EvaluateWithParentheses(expr As String, conditions As Object) As Boolean
Dim pos As Long, level As Long, startPos As Long
Dim i As Long
Dim innerExpr As String
Dim innerResult As Boolean
Dim newExpr As String
'
startPos = 0
level = 0
For i = 1 To Len(expr)
If Mid(expr, i, 1) = "(" Then
If level = 0 Then startPos = i
level = level + 1
ElseIf Mid(expr, i, 1) = ")" Then
level = level - 1
If level = 0 And startPos > 0 Then
' 找到一对括号
innerExpr = Mid(expr, startPos + 1, i - startPos - 1)
innerResult = EvaluateExpression(innerExpr, conditions)
'
newExpr = Left(expr, startPos - 1) & _
IIf(innerResult, "TRUE", "FALSE") & _
Mid(expr, i + 1)
' 递归处理剩余部分
EvaluateWithParentheses = EvaluateExpression(newExpr, conditions)
Exit Function
End If
End If
Next i
' 如果没有找到有效括号,直接计算
EvaluateWithParentheses = EvaluateExpression(expr, conditions)
End Function
' ========================================
' EvaluateOR 方法 (私有)
' 功能: 处理 OR 逻辑运算
' 规则: 任一为真则为真
' ========================================
Private Function EvaluateOR(expr As String, conditions As Object) As Boolean
Dim parts() As String
Dim part As Variant
' OR
parts = Split(expr, " OR ")
' 任一部分为真则返回真
For Each part In parts
If EvaluateExpression(Trim(CStr(part)), conditions) Then
EvaluateOR = True
Exit Function
End If
Next part
EvaluateOR = False
End Function
' ========================================
' EvaluateAND 方法 (私有)
' 功能: 处理 AND 逻辑运算
' 规则: 全部为真才为真
' ========================================
Private Function EvaluateAND(expr As String, conditions As Object) As Boolean
Dim parts() As String
Dim part As Variant
' AND
parts = Split(expr, " AND ")
' 全部部分为真才返回真
For Each part In parts
If Not EvaluateExpression(Trim(CStr(part)), conditions) Then
EvaluateAND = False
Exit Function
End If
Next part
EvaluateAND = True
End Function
' ========================================
' EvaluateSimpleCondition 方法 (私有)
' 功能: 计算单个条件表达式
' 支持: = (等于), != (不等于)
' 格式: varName=value 或 varName!=value
' ========================================
Private Function EvaluateSimpleCondition(cond As String, conditions As Object) As Boolean
cond = Trim(cond)
' TRUE/FALSE ()
If UCase(cond) = "TRUE" Then
EvaluateSimpleCondition = True
Exit Function
ElseIf UCase(cond) = "FALSE" Then
EvaluateSimpleCondition = False
Exit Function
End If
Dim varName As String
Dim expectedValue As String
Dim actualValue As String
Dim isNotEqual As Boolean
' != =
If InStr(cond, "!=") > 0 Then
isNotEqual = True
Dim parts1() As String
parts1 = Split(cond, "!=")
If UBound(parts1) < 1 Then
EvaluateSimpleCondition = False
Exit Function
End If
varName = Trim(parts1(0))
expectedValue = Trim(parts1(1))
ElseIf InStr(cond, "=") > 0 Then
isNotEqual = False
Dim parts2() As String
parts2 = Split(cond, "=")
If UBound(parts2) < 1 Then
EvaluateSimpleCondition = False
Exit Function
End If
varName = Trim(parts2(0))
expectedValue = Trim(parts2(1))
Else
' 无效的条件格式
EvaluateSimpleCondition = False
Exit Function
End If
'
If conditions.Exists(varName) Then
actualValue = Trim(CStr(conditions(varName)))
Else
actualValue = ""
End If
' 比较值 (不区分大小写)
Dim isEqual As Boolean
isEqual = (UCase(actualValue) = UCase(expectedValue))
' 返回结果
If isNotEqual Then
EvaluateSimpleCondition = Not isEqual
Else
EvaluateSimpleCondition = isEqual
End If
End Function
' ========================================
' TestExpression 方法
' 功能: 测试表达式是否有效 (用于调试)
' 参数: expr - 表达式字符串
' : String - "有效"
' ========================================
Public Function TestExpression(expr As String) As String
On Error GoTo ErrorHandler
'
Dim testConditions As Object
Set testConditions = CreateObject("Scripting.Dictionary")
testConditions("gclj") = "M20"
testConditions("jycz") = "3"
testConditions("lcfw") = "M16"
'
Dim result As Boolean
result = IsMatch(expr, testConditions)
TestExpression = "有效 (结果: " & IIf(result, "True", "False") & ")"
Exit Function
ErrorHandler:
TestExpression = "无效: " & Err.description
End Function

View File

@@ -0,0 +1,229 @@
' ========================================
' 类模块: clsModelParser
' 用途: 解析产品型号,提取各部分代码
' 示例: YTHN-100.A0.532.M203.M16.Y3|BP-095.2312.M16.PA3
' ========================================
Option Explicit
' ========================================
' 公共属性
' ========================================
Public RawModel As String ' 原始完整型号
Public HeaderModel As String ' 表头型号部分
Public DialModel As String ' 表盘型号部分
Public AccessoryModel As String ' 附件型号部分
Public FlangeModel As String ' 法兰隔膜型号部分
' 表头各部分
Public ModelType As String ' 型号 (如 YTHN)
Public Diameter As String ' 公称外径 (如 100)
Public InstallForm As String ' 安装形式 (如 A0)
Public ShellForm As String ' 壳体形式 (如 532)
Public ConnectionCode As String ' 过程连接&材质代码 (如 M203)
Public RangeCode As String ' 量程范围代码 (如 M16)
Public Characteristics As String ' 仪表特性 (如 Y3)
' ========================================
' 私有变量
' ========================================
Private m_IsValid As Boolean ' 解析是否成功
Private m_ErrorMessage As String ' 错误信息
' ========================================
' ParseModel 方法
' 功能: 解析产品型号字符串
' 参数: modelStr - 完整的产品型号字符串
' 返回: Boolean - 解析是否成功
' : parser.ParseModel("YTHN-100.A0.532.M203.M16.Y3|BP-095.2312.M16.PA3")
' ========================================
Public Function ParseModel(modelStr As String) As Boolean
On Error GoTo ErrorHandler
'
m_IsValid = False
m_ErrorMessage = ""
RawModel = Trim(modelStr)
'
If RawModel = "" Then
m_ErrorMessage = "型号字符串为空"
ParseModel = False
Exit Function
End If
' : |
Dim parts() As String
parts = Split(RawModel, "|")
If UBound(parts) >= 0 Then HeaderModel = Trim(parts(0))
If UBound(parts) >= 1 Then DialModel = Trim(parts(1))
If UBound(parts) >= 2 Then AccessoryModel = Trim(parts(2))
If UBound(parts) >= 3 Then FlangeModel = Trim(parts(3))
' : ()
If HeaderModel = "" Then
m_ErrorMessage = "表头型号为空"
ParseModel = False
Exit Function
End If
'
If Not ParseHeader(HeaderModel) Then
ParseModel = False
Exit Function
End If
m_IsValid = True
ParseModel = True
Exit Function
ErrorHandler:
m_ErrorMessage = "解析出错: " & Err.description
m_IsValid = False
ParseModel = False
End Function
' ========================================
' ParseHeader 方法 (私有)
' 功能: 解析表头型号部分
' 格式: [型号]-[公称外径].[安装形式].[壳体形式].[过程连接&材质].[量程范围].[仪表特性]
' 示例: YTHN-100.A0.532.M203.M16.Y3
' ========================================
Private Function ParseHeader(headerStr As String) As Boolean
On Error GoTo ErrorHandler
' -
Dim mainParts() As String
mainParts = Split(headerStr, "-")
If UBound(mainParts) < 1 Then
m_ErrorMessage = "表头格式错误: 缺少 - 分隔符"
ParseHeader = False
Exit Function
End If
' 提取型号
ModelType = Trim(mainParts(0))
' .
Dim params() As String
params = Split(mainParts(1), ".")
' (5)
If UBound(params) < 4 Then
m_ErrorMessage = "表头参数不足: 需要至少5个参数段"
ParseHeader = False
Exit Function
End If
' 提取各参数
Diameter = Trim(params(0)) ' 公称外径
InstallForm = Trim(params(1)) ' 安装形式
ShellForm = Trim(params(2)) ' 壳体形式
ConnectionCode = Trim(params(3)) ' 过程连接&材质
RangeCode = Trim(params(4)) ' 量程范围
' ()
If UBound(params) >= 5 Then
Characteristics = Trim(params(5))
Else
Characteristics = ""
End If
ParseHeader = True
Exit Function
ErrorHandler:
m_ErrorMessage = "解析表头出错: " & Err.description
ParseHeader = False
End Function
' ========================================
' GetThreadCode 方法
' 功能: 从过程连接代码中提取螺纹代码
' 规则: 去掉最后一位数字
' 示例: M203 -> M20
' ========================================
Public Function GetThreadCode() As String
If ConnectionCode = "" Then
GetThreadCode = ""
Exit Function
End If
' 去掉最后一位字符 (假设最后一位是材质代码)
If Len(ConnectionCode) > 1 Then
GetThreadCode = Left(ConnectionCode, Len(ConnectionCode) - 1)
Else
GetThreadCode = ConnectionCode
End If
End Function
' ========================================
' GetMaterialCode 方法
' 功能: 从过程连接代码中提取材质代码
' 规则: 取最后一位数字
' 示例: M203 -> 3
' ========================================
Public Function GetMaterialCode() As String
If ConnectionCode = "" Then
GetMaterialCode = ""
Exit Function
End If
' 取最后一位字符
GetMaterialCode = Right(ConnectionCode, 1)
End Function
' ========================================
' GetRangeCode 方法
' 功能: 获取量程代码
' 规则: 直接返回
' 示例: M16 -> M16
' ========================================
Public Function GetRangeCode() As String
GetRangeCode = RangeCode
End Function
' ========================================
' IsValid 属性
' 功能: 返回解析是否成功
' ========================================
Public Property Get IsValid() As Boolean
IsValid = m_IsValid
End Property
' ========================================
' ErrorMessage 属性
' 功能: 返回错误信息
' ========================================
Public Property Get ErrorMessage() As String
ErrorMessage = m_ErrorMessage
End Property
' ========================================
' ToString 方法
' 功能: 返回解析结果的字符串表示 (用于调试)
' ========================================
Public Function ToString() As String
Dim result As String
result = "【型号解析结果】" & vbCrLf
result = result & "原始型号: " & RawModel & vbCrLf
result = result & "表头型号: " & HeaderModel & vbCrLf
result = result & "表盘型号: " & DialModel & vbCrLf
result = result & vbCrLf
result = result & "【表头各部分】" & vbCrLf
result = result & " 型号: " & ModelType & vbCrLf
result = result & " 公称外径: " & Diameter & vbCrLf
result = result & " 安装形式: " & InstallForm & vbCrLf
result = result & " 壳体形式: " & ShellForm & vbCrLf
result = result & " 过程连接&材质: " & ConnectionCode & vbCrLf
result = result & " 量程范围: " & RangeCode & vbCrLf
result = result & " 仪表特性: " & Characteristics & vbCrLf
result = result & vbCrLf
result = result & "【提取代码】" & vbCrLf
result = result & " 螺纹代码: " & GetThreadCode() & vbCrLf
result = result & " 材质代码: " & GetMaterialCode() & vbCrLf
result = result & " 量程代码: " & GetRangeCode() & vbCrLf
ToString = result
End Function

View File

@@ -1,6 +1,6 @@
' ========================================
' 模块: modBOMTest
' 用途: 测试和使用BOM数据结构-同步测试2
' 用途: 测试和使用BOM数据结构
' ========================================
Option Explicit
@@ -19,19 +19,19 @@ Sub TestBOMStructure()
bomMgr.LoadData wsConfig, wsPlatform
' 打印类别树结构到新工作表
' Dim wsOutput As Worksheet
' On Error Resume Next
' Application.DisplayAlerts = False
' ThisWorkbook.Worksheets("BOM结构").Delete
' Application.DisplayAlerts = True
' On Error GoTo 0
'
' Set wsOutput = ThisWorkbook.Worksheets.Add
' wsOutput.Name = "BOM结构"
Dim wsOutput As Worksheet
On Error Resume Next
Application.DisplayAlerts = False
ThisWorkbook.Worksheets("BOM结构").Delete
Application.DisplayAlerts = True
On Error GoTo 0
Set wsOutput = ThisWorkbook.Worksheets.Add
wsOutput.Name = "BOM结构"
'bomMgr.PrintCategoryTree wsOutput
Dim cats As Collection
Dim cats As collection
Dim i As Integer
Set cats = bomMgr.GetRootCategories()
For i = 1 To cats.Count
@@ -40,7 +40,7 @@ Sub TestBOMStructure()
'MsgBox "BOM数据结构加载完成!" & vbCrLf & _
MsgBox "BOM数据结构加载完成!" & vbCrLf & _
"请查看 'BOM结构' 工作表", vbInformation
End Sub
@@ -55,19 +55,19 @@ Sub GetCategoryMaterials()
bomMgr.LoadData wsConfig, wsPlatform
' 获取"部件"类别的物料(使用父类别)
Dim Materials As Collection
Set Materials = bomMgr.GetMaterialsForPicking("部件", True)
Dim materials As collection
Set materials = bomMgr.GetMaterialsForPicking("部件", True)
Debug.Print "部件类别物料数量(父类别): " & Materials.Count
Debug.Print "部件类别物料数量(父类别): " & materials.Count
' 获取"部件"类别的物料(使用子类别)
Set Materials = bomMgr.GetMaterialsForPicking("部件", False)
Set materials = bomMgr.GetMaterialsForPicking("部件", False)
Debug.Print "部件类别物料数量(子类别展开): " & Materials.Count
Debug.Print "部件类别物料数量(子类别展开): " & materials.Count
' 遍历物料
Dim mat As clsMaterialItem
For Each mat In Materials
For Each mat In materials
Debug.Print mat.code & " - " & mat.Name & _
" | 数量:" & mat.Quantity & _
" | 条件:" & mat.Condition
@@ -98,36 +98,36 @@ Sub GeneratePickingList()
' 写入表头
Dim row As Long
row = 1
wsPickList.Cells(row, 1).Value = "代号"
wsPickList.Cells(row, 2).Value = "名称"
wsPickList.Cells(row, 3).Value = "类别"
wsPickList.Cells(row, 4).Value = "数量"
wsPickList.Cells(row, 5).Value = "选择条件"
wsPickList.Cells(row, 6).Value = "领料方式"
wsPickList.Cells(row, 1).value = "代号"
wsPickList.Cells(row, 2).value = "名称"
wsPickList.Cells(row, 3).value = "类别"
wsPickList.Cells(row, 4).value = "数量"
wsPickList.Cells(row, 5).value = "选择条件"
wsPickList.Cells(row, 6).value = "领料方式"
row = row + 1
' 遍历所有根类别
Dim rootCats As Collection
Dim rootCats As collection
Set rootCats = bomMgr.GetRootCategories
Dim cat As clsCategory
Dim Materials As Collection
Dim materials As collection
Dim mat As clsMaterialItem
Dim i As Long, j As Long
For i = 1 To rootCats.Count
Set cat = rootCats(i)
' 默认使用父类别物料
Set Materials = bomMgr.GetMaterialsForPicking(cat.categoryName, True)
Set materials = bomMgr.GetMaterialsForPicking(cat.categoryName, True)
For j = 1 To Materials.Count
Set mat = Materials(j)
wsPickList.Cells(row, 1).Value = mat.code
wsPickList.Cells(row, 2).Value = mat.Name
wsPickList.Cells(row, 3).Value = mat.Category
wsPickList.Cells(row, 4).Value = mat.Quantity
wsPickList.Cells(row, 5).Value = mat.Condition
wsPickList.Cells(row, 6).Value = "父类别"
For j = 1 To materials.Count
Set mat = materials(j)
wsPickList.Cells(row, 1).value = mat.code
wsPickList.Cells(row, 2).value = mat.Name
wsPickList.Cells(row, 3).value = mat.Category
wsPickList.Cells(row, 4).value = mat.Quantity
wsPickList.Cells(row, 5).value = mat.Condition
wsPickList.Cells(row, 6).value = "父类别"
row = row + 1
Next j
Next i
@@ -139,64 +139,6 @@ Sub GeneratePickingList()
MsgBox "领料清单生成完成!", vbInformation
End Sub
' 示例: 在立即窗口打印领料清单
Sub PrintPickingList()
Dim bomMgr As New clsBOMManager
Dim wsConfig As Worksheet, wsPlatform As Worksheet
Set wsConfig = ThisWorkbook.Worksheets("领料配置")
Set wsPlatform = ThisWorkbook.Worksheets("平台配置清单")
bomMgr.LoadData wsConfig, wsPlatform
' 打印表头
Debug.Print String(80, "=")
Debug.Print "领料清单"
Debug.Print String(80, "=")
' 遍历所有根类别
Dim rootCats As Collection
Set rootCats = bomMgr.GetRootCategories
Dim cat As clsCategory
Dim Materials As Collection
Dim mat As clsMaterialItem
Dim i As Long, j As Long
Dim totalCount As Long
totalCount = 0
For i = 1 To rootCats.Count
Set cat = rootCats(i)
' 打印类别分隔符
Debug.Print ""
Debug.Print "【类别: " & cat.categoryName & "】"
Debug.Print String(40, "-")
' 默认使用父类别物料
Set Materials = bomMgr.GetMaterialsForPicking(cat.categoryName, True)
For j = 1 To Materials.Count
Set mat = Materials(j)
Debug.Print " " & mat.code & _
String(20 - Len(mat.code), " ") & _
mat.Name & _
" | 数量:" & mat.Quantity & _
" | 条件:" & IIf(mat.Condition = "", "(无)", mat.Condition)
totalCount = totalCount + 1
Next j
Debug.Print " 小计: " & Materials.Count & " 项"
Next i
' 打印汇总
Debug.Print ""
Debug.Print String(80, "=")
Debug.Print "总计: " & totalCount & " 项物料"
Debug.Print String(80, "=")
End Sub
' 示例: 查询特定物料信息
Sub QueryMaterialInfo()
Dim bomMgr As New clsBOMManager
@@ -214,15 +156,15 @@ Sub QueryMaterialInfo()
If Not cat Is Nothing Then
Debug.Print "类别: " & cat.categoryName
Debug.Print "父类别: " & cat.ParentCategoryName
Debug.Print "物料数: " & cat.Materials.Count
Debug.Print "物料数: " & cat.materials.Count
Debug.Print "子类别数: " & cat.SubCategories.Count
Debug.Print "是否叶子类别: " & cat.IsLeafCategory
' 列出所有物料
Dim mat As clsMaterialItem
Dim i As Long
For i = 1 To cat.Materials.Count
Set mat = cat.Materials(i)
For i = 1 To cat.materials.Count
Set mat = cat.materials(i)
Debug.Print " - " & mat.code & ": " & mat.Name
Next i
@@ -232,7 +174,7 @@ Sub QueryMaterialInfo()
For j = 1 To cat.SubCategories.Count
Set subCat = cat.SubCategories(j)
Debug.Print " 子类别: " & subCat.categoryName & _
" (物料数:" & subCat.Materials.Count & ")"
" (物料数:" & subCat.materials.Count & ")"
Next j
End If
End Sub

View File

@@ -0,0 +1,375 @@
' ========================================
' 模块: modModelParserExamples
' 用途: 型号解析与物料匹配的实际应用示例
' ========================================
Option Explicit
' ========================================
' 示例1: 根据型号生成完整的领料清单
' ========================================
Sub Example1_GeneratePickingListByModel()
Dim bomMgr As New clsBOMManager
Dim wsConfig As Worksheet
Dim wsPlatform As Worksheet
' 加载配置
Set wsConfig = ThisWorkbook.Worksheets("领料配置")
Set wsPlatform = ThisWorkbook.Worksheets("平台配置清单")
bomMgr.LoadData wsConfig, wsPlatform
' 产品型号
Dim modelStr As String
modelStr = "YTHN-100.A0.532.M203.M16.Y3|BP-095.2312.M16.PA3"
' 创建领料清单工作表
Dim wsPickList As Worksheet
On Error Resume Next
Application.DisplayAlerts = False
ThisWorkbook.Worksheets("型号领料清单").Delete
Application.DisplayAlerts = True
On Error GoTo 0
Set wsPickList = ThisWorkbook.Worksheets.Add
wsPickList.Name = "型号领料清单"
' 写入表头
Dim row As Long
row = 1
wsPickList.Cells(row, 1).value = "产品型号"
wsPickList.Cells(row, 2).value = modelStr
row = row + 1
' 提取条件并显示
Dim conditions As Object
Set conditions = bomMgr.ParseModelAndExtractConditions(modelStr)
wsPickList.Cells(row, 1).value = "提取条件"
Dim condStr As String
Dim key As Variant
For Each key In conditions.Keys
condStr = condStr & key & "=" & conditions(key) & "; "
Next key
wsPickList.Cells(row, 2).value = condStr
row = row + 2
' 表头
wsPickList.Cells(row, 1).value = "类别"
wsPickList.Cells(row, 2).value = "代号"
wsPickList.Cells(row, 3).value = "名称"
wsPickList.Cells(row, 4).value = "数量"
wsPickList.Cells(row, 5).value = "选择条件"
wsPickList.Cells(row, 6).value = "匹配状态"
wsPickList.Range("A" & row & ":F" & row).Font.Bold = True
row = row + 1
' 遍历所有根类别
Dim rootCats As collection
Set rootCats = bomMgr.GetRootCategories
Dim cat As clsCategory
Dim materials As collection
Dim mat As clsMaterialItem
Dim i As Long
For i = 1 To rootCats.Count
Set cat = rootCats(i)
' 获取该类别符合条件的物料
Set materials = bomMgr.GetMaterialsByModel(modelStr, cat.categoryName)
' 写入物料
Dim j As Long
For j = 1 To materials.Count
Set mat = materials(j)
wsPickList.Cells(row, 1).value = cat.categoryName
wsPickList.Cells(row, 2).value = mat.code
wsPickList.Cells(row, 3).value = mat.Name
wsPickList.Cells(row, 4).value = mat.Quantity
wsPickList.Cells(row, 5).value = IIf(mat.Condition = "", "(无条件)", mat.Condition)
wsPickList.Cells(row, 6).value = "?"
row = row + 1
Next j
Next i
' 格式化
wsPickList.Columns("A:F").AutoFit
MsgBox "领料清单生成完成!" & vbCrLf & _
"请查看工作表: 型号领料清单", vbInformation
End Sub
' ========================================
' 示例2: 批量处理多个型号
' ========================================
Sub Example2_BatchProcessModels()
Dim bomMgr As New clsBOMManager
Dim wsConfig As Worksheet
Dim wsPlatform As Worksheet
' 加载配置
Set wsConfig = ThisWorkbook.Worksheets("领料配置")
Set wsPlatform = ThisWorkbook.Worksheets("平台配置清单")
bomMgr.LoadData wsConfig, wsPlatform
' 型号列表
Dim models() As String
models = Split("YTHN-100.A0.532.M203.M16.Y3|BP-095.2312.M16.PA3," & _
"YTHN-100.A0.532.M203.M02.Y3|BP-095.2312.M02.PA3," & _
"YTHN-100.A0.532.M203.M12.Y3|BP-095.2312.M12.PA3", ",")
' 创建汇总表
Dim wsReport As Worksheet
On Error Resume Next
Application.DisplayAlerts = False
ThisWorkbook.Worksheets("批量型号汇总").Delete
Application.DisplayAlerts = True
On Error GoTo 0
Set wsReport = ThisWorkbook.Worksheets.Add
wsReport.Name = "批量型号汇总"
' 表头
Dim row As Long
row = 1
wsReport.Cells(row, 1).value = "型号"
wsReport.Cells(row, 2).value = "提取条件"
wsReport.Cells(row, 3).value = "匹配物料数"
wsReport.Cells(row, 4).value = "部件物料"
wsReport.Range("A1:D1").Font.Bold = True
row = row + 1
' 处理每个型号
Dim modelStr As String
Dim i As Long
For i = LBound(models) To UBound(models)
modelStr = Trim(models(i))
If modelStr <> "" Then
' 提取条件
Dim conditions As Object
Set conditions = bomMgr.ParseModelAndExtractConditions(modelStr)
Dim condStr As String
condStr = ""
Dim key As Variant
For Each key In conditions.Keys
condStr = condStr & key & "=" & conditions(key) & "; "
Next key
' 获取物料
Dim materials As collection
Set materials = bomMgr.GetMaterialsByModel(modelStr, "部件")
' 写入结果
wsReport.Cells(row, 1).value = modelStr
wsReport.Cells(row, 2).value = condStr
wsReport.Cells(row, 3).value = materials.Count
' 列出部件物料
Dim matList As String
matList = ""
Dim mat As clsMaterialItem
For Each mat In materials
matList = matList & mat.code & "(" & mat.Name & "); "
Next mat
wsReport.Cells(row, 4).value = matList
row = row + 1
End If
Next i
' 格式化
wsReport.Columns("A:D").AutoFit
MsgBox "批量处理完成!", vbInformation
End Sub
' ========================================
' 示例3: 查询并显示某个型号的详细信息
' ========================================
Sub Example3_ShowModelDetails()
' 弹出输入框
Dim modelStr As String
modelStr = InputBox("请输入产品型号:", "型号查询", _
"YTHN-100.A0.532.M203.M16.Y3")
If modelStr = "" Then Exit Sub
' 解析型号
Dim parser As New clsModelParser
If Not parser.ParseModel(modelStr) Then
MsgBox "型号解析失败: " & parser.ErrorMessage, vbCritical
Exit Sub
End If
' 提取条件
Dim extractor As New clsConditionExtractor
Dim conditions As Object
Set conditions = extractor.ExtractConditions(parser)
' 显示详细信息
Dim msg As String
msg = "【型号解析结果】" & vbCrLf & vbCrLf
msg = msg & "原始型号: " & parser.RawModel & vbCrLf
msg = msg & "表头型号: " & parser.HeaderModel & vbCrLf
msg = msg & "表盘型号: " & parser.DialModel & vbCrLf & vbCrLf
msg = msg & "【表头各部分】" & vbCrLf
msg = msg & "型号: " & parser.ModelType & vbCrLf
msg = msg & "公称外径: " & parser.Diameter & vbCrLf
msg = msg & "安装形式: " & parser.InstallForm & vbCrLf
msg = msg & "壳体形式: " & parser.ShellForm & vbCrLf
msg = msg & "过程连接&材质: " & parser.ConnectionCode & vbCrLf
msg = msg & "量程范围: " & parser.RangeCode & vbCrLf
msg = msg & "仪表特性: " & parser.Characteristics & vbCrLf & vbCrLf
msg = msg & "【提取的物料选择条件】" & vbCrLf
Dim key As Variant
For Each key In conditions.Keys
msg = msg & key & " = " & conditions(key) & vbCrLf
Next key
MsgBox msg, vbInformation, "型号详细信息"
End Sub
' ========================================
' 示例4: 对比两个型号的差异
' ========================================
Sub Example4_CompareModels()
Dim model1 As String, model2 As String
model1 = InputBox("请输入第一个型号:", "型号对比", _
"YTHN-100.A0.532.M203.M16.Y3")
If model1 = "" Then Exit Sub
model2 = InputBox("请输入第二个型号:", "型号对比", _
"YTHN-100.A0.532.M203.M02.Y3")
If model2 = "" Then Exit Sub
' 解析两个型号
Dim parser1 As New clsModelParser
Dim parser2 As New clsModelParser
Dim extractor As New clsConditionExtractor
parser1.ParseModel model1
parser2.ParseModel model2
Dim cond1 As Object, cond2 As Object
Set cond1 = extractor.ExtractConditions(parser1)
Set extractor = New clsConditionExtractor
Set cond2 = extractor.ExtractConditions(parser2)
' 对比
Dim msg As String
msg = "【型号对比】" & vbCrLf & vbCrLf
msg = msg & "型号1: " & model1 & vbCrLf
msg = msg & "型号2: " & model2 & vbCrLf & vbCrLf
msg = msg & "【条件差异】" & vbCrLf
Dim key As Variant
Dim allKeys As Object
Set allKeys = CreateObject("Scripting.Dictionary")
For Each key In cond1.Keys
allKeys(key) = True
Next key
For Each key In cond2.Keys
allKeys(key) = True
Next key
For Each key In allKeys.Keys
Dim val1 As String, val2 As String
val1 = ""
val2 = ""
If cond1.Exists(key) Then val1 = cond1(key)
If cond2.Exists(key) Then val2 = cond2(key)
If val1 <> val2 Then
msg = msg & key & ": " & val1 & " → " & val2 & " ?" & vbCrLf
Else
msg = msg & key & ": " & val1 & " ?" & vbCrLf
End If
Next key
MsgBox msg, vbInformation, "型号对比结果"
End Sub
' ========================================
' 示例5: 验证物料选择条件的有效性
' ========================================
Sub Example5_ValidateMaterialConditions()
Dim bomMgr As New clsBOMManager
Dim wsConfig As Worksheet
Dim wsPlatform As Worksheet
' 加载配置
Set wsConfig = ThisWorkbook.Worksheets("领料配置")
Set wsPlatform = ThisWorkbook.Worksheets("平台配置清单")
bomMgr.LoadData wsConfig, wsPlatform
' 创建验证结果表
Dim wsValidation As Worksheet
On Error Resume Next
Application.DisplayAlerts = False
ThisWorkbook.Worksheets("条件验证结果").Delete
Application.DisplayAlerts = True
On Error GoTo 0
Set wsValidation = ThisWorkbook.Worksheets.Add
wsValidation.Name = "条件验证结果"
' 表头
Dim row As Long
row = 1
wsValidation.Cells(row, 1).value = "代号"
wsValidation.Cells(row, 2).value = "名称"
wsValidation.Cells(row, 3).value = "选择条件"
wsValidation.Cells(row, 4).value = "验证结果"
wsValidation.Range("A1:D1").Font.Bold = True
row = row + 1
' 获取所有根类别
Dim rootCats As collection
Set rootCats = bomMgr.GetRootCategories
Dim cat As clsCategory
Dim materials As collection
Dim mat As clsMaterialItem
Dim matcher As New clsConditionMatcher
Dim i As Long
' 遍历所有物料
For i = 1 To rootCats.Count
Set cat = rootCats(i)
Set materials = New collection
' 收集该类别的所有物料
Dim j As Long
For j = 1 To cat.materials.Count
materials.Add cat.materials(j)
Next j
' 验证每个物料的条件
For Each mat In materials
wsValidation.Cells(row, 1).value = mat.code
wsValidation.Cells(row, 2).value = mat.Name
wsValidation.Cells(row, 3).value = IIf(mat.Condition = "", "(无)", mat.Condition)
If mat.Condition = "" Then
wsValidation.Cells(row, 4).value = "? 无条件"
Else
Dim validResult As String
validResult = matcher.TestExpression(mat.Condition)
wsValidation.Cells(row, 4).value = validResult
End If
row = row + 1
Next mat
Next i
' 格式化
wsValidation.Columns("A:D").AutoFit
MsgBox "条件验证完成!", vbInformation
End Sub

View File

@@ -0,0 +1,309 @@
' ========================================
' 模块: modModelParserTest
' 用途: 测试型号解析和物料匹配功能
' ========================================
Option Explicit
' ========================================
' 测试1: 型号解析器基础功能
' ========================================
Sub Test1_ModelParser()
Debug.Print String(80, "=")
Debug.Print "测试1: 型号解析器基础功能"
Debug.Print String(80, "=")
Dim parser As New clsModelParser
Dim modelStr As String
' 测试用例1: 完整型号
modelStr = "YTHN-100.A0.532.M203.M16.Y3|BP-095.2312.M16.PA3"
Debug.Print "【测试用例1】完整型号"
Debug.Print "输入: " & modelStr
If parser.ParseModel(modelStr) Then
Debug.Print parser.ToString()
Debug.Print "? 解析成功"
Else
Debug.Print "? 解析失败: " & parser.ErrorMessage
End If
Debug.Print ""
' 测试用例2: 仅表头
modelStr = "YTHN-100.A0.532.M203.M16.Y3"
Debug.Print "【测试用例2】仅表头"
Debug.Print "输入: " & modelStr
If parser.ParseModel(modelStr) Then
Debug.Print " 螺纹代码: " & parser.GetThreadCode()
Debug.Print " 材质代码: " & parser.GetMaterialCode()
Debug.Print " 量程代码: " & parser.GetRangeCode()
Debug.Print "? 解析成功"
Else
Debug.Print "? 解析失败: " & parser.ErrorMessage
End If
Debug.Print String(80, "=")
Debug.Print ""
End Sub
' ========================================
' 测试2: 条件提取器
' ========================================
Sub Test2_ConditionExtractor()
Debug.Print String(80, "=")
Debug.Print "测试2: 条件提取器"
Debug.Print String(80, "=")
Dim parser As New clsModelParser
Dim extractor As New clsConditionExtractor
Dim modelStr As String
modelStr = "YTHN-100.A0.532.M203.M16.Y3"
Debug.Print "输入型号: " & modelStr
If parser.ParseModel(modelStr) Then
Dim conditions As Object
Set conditions = extractor.ExtractConditions(parser)
Debug.Print extractor.ToString()
' 验证提取结果
Debug.Print "【验证】"
Debug.Print " gclj = " & extractor.GetConditionValue("gclj") & _
IIf(extractor.GetConditionValue("gclj") = "M20", " ?", " ?")
Debug.Print " jycz = " & extractor.GetConditionValue("jycz") & _
IIf(extractor.GetConditionValue("jycz") = "3", " ?", " ?")
Debug.Print " lcfw = " & extractor.GetConditionValue("lcfw") & _
IIf(extractor.GetConditionValue("lcfw") = "M16", " ?", " ?")
Else
Debug.Print "? 型号解析失败"
End If
Debug.Print String(80, "=")
Debug.Print ""
End Sub
' ========================================
' 测试3: 条件匹配器
' ========================================
Sub Test3_ConditionMatcher()
Debug.Print String(80, "=")
Debug.Print "测试3: 条件匹配器"
Debug.Print String(80, "=")
Dim matcher As New clsConditionMatcher
Dim conditions As Object
Set conditions = CreateObject("Scripting.Dictionary")
conditions("gclj") = "M20"
conditions("jycz") = "3"
conditions("lcfw") = "M16"
Debug.Print "【测试条件】"
Debug.Print " gclj = M20"
Debug.Print " jycz = 3"
Debug.Print " lcfw = M16"
Debug.Print ""
' 测试用例
Dim testCases As Variant
testCases = Array( _
Array("", True, "空条件"), _
Array("lcfw=M16", True, "简单等于"), _
Array("lcfw=M17", False, "简单不匹配"), _
Array("lcfw=M16 AND gclj=M20", True, "AND 全真"), _
Array("lcfw=M16 AND gclj=M10", False, "AND 一假"), _
Array("lcfw=M16 OR lcfw=M17", True, "OR 一真"), _
Array("lcfw=M15 OR lcfw=M17", False, "OR 全假"), _
Array("gclj!=M10", True, "不等于 真"), _
Array("gclj!=M20", False, "不等于 假"), _
Array("lcfw=M02 AND gclj!=M20", False, "复合条件1"), _
Array("lcfw=M16 AND gclj!=M10", True, "复合条件2"), _
Array("gclj=M20 AND (lcfw=M16 OR lcfw=M17)", True, "括号优先级1"), _
Array("gclj=M20 AND (lcfw=M15 OR lcfw=M17)", False, "括号优先级2") _
)
Dim i As Long
Dim testCase As Variant
Dim expr As String
Dim expected As Boolean
Dim actual As Boolean
Dim description As String
Dim passCount As Long
Dim failCount As Long
passCount = 0
failCount = 0
Debug.Print "【测试用例】"
For i = LBound(testCases) To UBound(testCases)
testCase = testCases(i)
expr = testCase(0)
expected = testCase(1)
description = testCase(2)
actual = matcher.IsMatch(expr, conditions)
If actual = expected Then
Debug.Print " ? " & description & ": " & IIf(expr = "", "(空)", expr)
passCount = passCount + 1
Else
Debug.Print " ? " & description & ": " & expr
Debug.Print " 预期: " & expected & ", 实际: " & actual
failCount = failCount + 1
End If
Next i
Debug.Print ""
Debug.Print "【统计】"
Debug.Print " 通过: " & passCount
Debug.Print " 失败: " & failCount
Debug.Print String(80, "=")
Debug.Print ""
End Sub
' ========================================
' 测试4: 完整流程 - 根据型号获取物料
' ========================================
Sub Test4_GetMaterialsByModel()
Debug.Print String(80, "=")
Debug.Print "测试4: 根据型号获取物料(完整流程)"
Debug.Print String(80, "=")
' 加载BOM数据
Dim bomMgr As New clsBOMManager
Dim wsConfig As Worksheet
Dim wsPlatform As Worksheet
On Error Resume Next
Set wsConfig = ThisWorkbook.Worksheets("领料配置")
Set wsPlatform = ThisWorkbook.Worksheets("平台配置清单")
On Error GoTo 0
If wsConfig Is Nothing Or wsPlatform Is Nothing Then
Debug.Print "? 错误: 找不到必需的工作表"
Exit Sub
End If
bomMgr.LoadData wsConfig, wsPlatform
Debug.Print "? BOM数据加载完成"
Debug.Print ""
' 测试型号
Dim modelStr As String
modelStr = "YTHN-100.A0.532.M203.M16.Y3|BP-095.2312.M16.PA3"
' 获取"部件"类别的符合条件的物料
Dim materials As collection
Set materials = bomMgr.GetMaterialsByModel(modelStr, "部件")
Debug.Print ""
Debug.Print "【结果验证】"
If materials.Count = 1 Then
Dim mat As clsMaterialItem
Set mat = materials(1)
If mat.code = "01011019018" And mat.Name = "高压接头部件" Then
Debug.Print "? 测试通过!成功匹配到正确的物料"
Debug.Print " 代号: " & mat.code
Debug.Print " 名称: " & mat.Name
Debug.Print " 条件: " & mat.Condition
Else
Debug.Print "? 匹配到的物料不正确"
End If
Else
Debug.Print "? 匹配数量不正确预期1个实际" & materials.Count & "个"
End If
Debug.Print String(80, "=")
Debug.Print ""
End Sub
' ========================================
' 测试5: 获取所有类别的符合条件的物料
' ========================================
Sub Test5_GetAllMaterialsByModel()
Debug.Print String(80, "=")
Debug.Print "测试5: 获取所有类别的符合条件的物料"
Debug.Print String(80, "=")
' 加载BOM数据
Dim bomMgr As New clsBOMManager
Dim wsConfig As Worksheet
Dim wsPlatform As Worksheet
On Error Resume Next
Set wsConfig = ThisWorkbook.Worksheets("领料配置")
Set wsPlatform = ThisWorkbook.Worksheets("平台配置清单")
On Error GoTo 0
If wsConfig Is Nothing Or wsPlatform Is Nothing Then
Debug.Print "? 错误: 找不到必需的工作表"
Exit Sub
End If
bomMgr.LoadData wsConfig, wsPlatform
' 测试型号
Dim modelStr As String
modelStr = "YTHN-100.A0.532.M203.M16.Y3"
' 获取所有类别的符合条件的物料
Dim materials As collection
Set materials = bomMgr.GetMaterialsByModel(modelStr)
Debug.Print ""
Debug.Print "【匹配结果汇总】"
Debug.Print " 共匹配 " & materials.Count & " 个物料"
Debug.Print String(80, "=")
Debug.Print ""
End Sub
' ========================================
' 运行所有测试
' ========================================
Sub RunAllTests()
Debug.Print vbCrLf & vbCrLf
Debug.Print "╔" & String(78, "═") & "╗"
Debug.Print "║" & Space(20) & "型号解析与物料匹配 - 完整测试套件" & Space(20) & "║"
Debug.Print "╚" & String(78, "═") & "╝"
Debug.Print ""
Test1_ModelParser
Test2_ConditionExtractor
Test3_ConditionMatcher
Test4_GetMaterialsByModel
Test5_GetAllMaterialsByModel
Debug.Print "╔" & String(78, "═") & "╗"
Debug.Print "║" & Space(30) & "所有测试完成" & Space(30) & "║"
Debug.Print "╚" & String(78, "═") & "╝"
End Sub
' ========================================
' 测试6: 条件提取规则展示
' ========================================
Sub Test6_ShowExtractionRules()
Debug.Print String(80, "=")
Debug.Print "测试6: 当前配置的条件提取规则"
Debug.Print String(80, "=")
Dim extractor As New clsConditionExtractor
Dim rules As Object
Set rules = extractor.GetExtractionRules()
Debug.Print "【提取规则配置】"
Dim key As Variant
For Each key In rules.Keys
Dim ruleInfo As Variant
ruleInfo = rules(key)
Debug.Print " 变量名: " & key
Debug.Print " 源字段: " & ruleInfo(0)
Debug.Print " 提取方法: " & ruleInfo(1)
Debug.Print ""
Next key
Debug.Print String(80, "=")
Debug.Print ""
End Sub

View File

@@ -31,12 +31,6 @@
"attributes": {},
"file": "DocumentModules\\Sheet4.cls"
},
"Sheet6.cls": {
"name": "Sheet6",
"type": "DocumentModules",
"attributes": {},
"file": "DocumentModules\\Sheet6.cls"
},
"clsCategory.cls": {
"name": "clsCategory",
"type": "ClassModules",
@@ -60,6 +54,42 @@
"type": "ClassModules",
"attributes": {},
"file": "ClassModules\\clsBOMManager.cls"
},
"Sheet6.cls": {
"name": "Sheet6",
"type": "DocumentModules",
"attributes": {},
"file": "DocumentModules\\Sheet6.cls"
},
"clsModelParser.cls": {
"name": "clsModelParser",
"type": "ClassModules",
"attributes": {},
"file": "ClassModules\\clsModelParser.cls"
},
"clsConditionExtractor.cls": {
"name": "clsConditionExtractor",
"type": "ClassModules",
"attributes": {},
"file": "ClassModules\\clsConditionExtractor.cls"
},
"clsConditionMatcher.cls": {
"name": "clsConditionMatcher",
"type": "ClassModules",
"attributes": {},
"file": "ClassModules\\clsConditionMatcher.cls"
},
"modModelParserTest.bas": {
"name": "modModelParserTest",
"type": "Modules",
"attributes": {},
"file": "Modules\\modModelParserTest.bas"
},
"modModelParserExamples.bas": {
"name": "modModelParserExamples",
"type": "Modules",
"attributes": {},
"file": "Modules\\modModelParserExamples.bas"
}
}
}

View File

@@ -0,0 +1,428 @@
# 型号解析与物料匹配系统 - 使用说明
## 📋 目录
1. [系统概述](#系统概述)
2. [安装部署](#安装部署)
3. [快速开始](#快速开始)
4. [详细使用](#详细使用)
5. [API参考](#api参考)
6. [扩展指南](#扩展指南)
7. [常见问题](#常见问题)
---
## 系统概述
### 功能简介
本系统实现了从产品型号中自动提取物料选择条件并根据这些条件从BOM库中筛选出符合要求的物料清单。
### 核心功能
-**型号解析**: 自动解析产品型号的各个组成部分
-**条件提取**: 按规则提取物料选择条件(过程连接、接液材质、量程范围等)
-**条件匹配**: 支持复杂的逻辑表达式(AND/OR/NOT/括号)
-**物料筛选**: 自动筛选符合条件的物料
-**批量处理**: 支持批量处理多个型号
-**可扩展性**: 便于添加新的提取条件和匹配规则
### 示例
**输入型号**: `YTHN-100.A0.532.M203.M16.Y3|BP-095.2312.M16.PA3`
**提取条件**:
- `gclj` (过程连接) = `M20`
- `jycz` (接液材质) = `3`
- `lcfw` (量程范围) = `M16`
**匹配结果**: `01011019018 - 高压接头部件`
---
## 安装部署
### 1. 导入类模块
将以下4个类模块文件导入到VBA项目中:
1. **clsModelParser.cls** - 型号解析器
2. **clsConditionExtractor.cls** - 条件提取器
3. **clsConditionMatcher.cls** - 条件匹配器
4. **clsBOMManager.cls** - BOM管理器(扩展版本)
### 2. 导入标准模块
将以下2个标准模块导入到VBA项目中:
1. **modModelParserTest.bas** - 测试模块
2. **modModelParserExamples.bas** - 应用示例模块
### 3. 验证安装
运行测试过程验证安装是否成功:
```vba
' 在立即窗口或通过运行按钮执行
RunAllTests
```
如果所有测试通过,说明安装成功!
---
## 快速开始
### 最简单的使用方式
```vba
Sub QuickStart()
' 1. 创建BOM管理器并加载数据
Dim bomMgr As New clsBOMManager
bomMgr.LoadData ThisWorkbook.Worksheets("领料配置"), _
ThisWorkbook.Worksheets("平台配置清单")
' 2. 根据型号获取物料
Dim modelStr As String
modelStr = "YTHN-100.A0.532.M203.M16.Y3"
Dim materials As Collection
Set materials = bomMgr.GetMaterialsByModel(modelStr, "部件")
' 3. 查看结果
Dim mat As clsMaterialItem
For Each mat In materials
Debug.Print mat.Code & " - " & mat.Name
Next mat
End Sub
```
### 生成领料清单
运行以下示例生成完整的领料清单:
```vba
Example1_GeneratePickingListByModel
```
这将创建一个新工作表,包含完整的领料清单。
---
## 详细使用
### 1. 型号解析
#### 基础解析
```vba
Dim parser As New clsModelParser
Dim modelStr As String
modelStr = "YTHN-100.A0.532.M203.M16.Y3|BP-095.2312.M16.PA3"
If parser.ParseModel(modelStr) Then
' 解析成功
Debug.Print "型号: " & parser.ModelType ' YTHN
Debug.Print "外径: " & parser.Diameter ' 100
Debug.Print "过程连接: " & parser.ConnectionCode ' M203
Debug.Print "量程: " & parser.RangeCode ' M16
Else
' 解析失败
Debug.Print "错误: " & parser.ErrorMessage
End If
```
#### 获取提取的代码
```vba
Debug.Print "螺纹代码: " & parser.GetThreadCode() ' M20
Debug.Print "材质代码: " & parser.GetMaterialCode() ' 3
Debug.Print "量程代码: " & parser.GetRangeCode() ' M16
```
### 2. 条件提取
```vba
Dim parser As New clsModelParser
parser.ParseModel "YTHN-100.A0.532.M203.M16.Y3"
Dim extractor As New clsConditionExtractor
Dim conditions As Object
Set conditions = extractor.ExtractConditions(parser)
' 获取单个条件
Debug.Print "过程连接: " & extractor.GetConditionValue("gclj") ' M20
Debug.Print "接液材质: " & extractor.GetConditionValue("jycz") ' 3
Debug.Print "量程范围: " & extractor.GetConditionValue("lcfw") ' M16
' 遍历所有条件
Dim key As Variant
For Each key In conditions.Keys
Debug.Print key & " = " & conditions(key)
Next key
```
### 3. 条件匹配
#### 简单条件
```vba
Dim matcher As New clsConditionMatcher
Dim conditions As Object
Set conditions = CreateObject("Scripting.Dictionary")
conditions("gclj") = "M20"
conditions("lcfw") = "M16"
' 等于
Debug.Print matcher.IsMatch("lcfw=M16", conditions) ' True
' 不等于
Debug.Print matcher.IsMatch("gclj!=M10", conditions) ' True
```
#### 复合条件
```vba
' AND 运算
Debug.Print matcher.IsMatch("lcfw=M16 AND gclj=M20", conditions) ' True
' OR 运算
Debug.Print matcher.IsMatch("lcfw=M16 OR lcfw=M17", conditions) ' True
' 括号优先级
Debug.Print matcher.IsMatch("gclj=M20 AND (lcfw=M16 OR lcfw=M17)", conditions) ' True
```
### 4. 获取物料清单
#### 指定类别
```vba
Dim bomMgr As New clsBOMManager
bomMgr.LoadData wsConfig, wsPlatform
' 获取"部件"类别的符合条件的物料
Dim materials As Collection
Set materials = bomMgr.GetMaterialsByModel("YTHN-100.A0.532.M203.M16.Y3", "部件")
```
#### 所有类别
```vba
' 获取所有类别的符合条件的物料
Set materials = bomMgr.GetMaterialsByModel("YTHN-100.A0.532.M203.M16.Y3")
```
#### 使用父类别/子类别逻辑
```vba
' 使用父类别物料(默认)
Set materials = bomMgr.GetMaterialsByCategoryAndModel( _
"YTHN-100.A0.532.M203.M16.Y3", "部件", True)
' 使用子类别物料(库存不足时)
Set materials = bomMgr.GetMaterialsByCategoryAndModel( _
"YTHN-100.A0.532.M203.M16.Y3", "部件", False)
```
---
## API参考
### clsModelParser
| 方法/属性 | 说明 | 返回类型 |
|---------|------|---------|
| `ParseModel(modelStr)` | 解析型号字符串 | Boolean |
| `GetThreadCode()` | 获取螺纹代码 | String |
| `GetMaterialCode()` | 获取材质代码 | String |
| `GetRangeCode()` | 获取量程代码 | String |
| `IsValid` | 解析是否成功 | Boolean |
| `ErrorMessage` | 错误信息 | String |
| `ToString()` | 返回解析结果(调试用) | String |
### clsConditionExtractor
| 方法/属性 | 说明 | 返回类型 |
|---------|------|---------|
| `ExtractConditions(parser)` | 提取所有条件 | Dictionary |
| `GetConditionValue(varName)` | 获取单个条件值 | String |
| `AddCondition(varName, value)` | 手动添加条件 | - |
| `AddExtractionRule(...)` | 添加提取规则 | - |
| `ToString()` | 返回条件字符串(调试用) | String |
### clsConditionMatcher
| 方法 | 说明 | 返回类型 |
|-----|------|---------|
| `IsMatch(expr, conditions)` | 判断条件是否匹配 | Boolean |
| `TestExpression(expr)` | 测试表达式有效性 | String |
### clsBOMManager (新增方法)
| 方法 | 说明 | 返回类型 |
|-----|------|---------|
| `GetMaterialsByModel(modelStr, [categoryName])` | 根据型号获取物料 | Collection |
| `GetMaterialsByCategoryAndModel(...)` | 根据类别和型号获取物料 | Collection |
| `ParseModelAndExtractConditions(modelStr)` | 解析型号并返回条件 | Dictionary |
---
## 扩展指南
### 添加新的提取条件
假设需要添加"表盘类型"条件:
```vba
' 在 clsConditionExtractor.InitializeRules 方法中添加:
m_ExtractionRules("bplx") = Array("DialCode", "Direct")
```
### 添加新的提取方法
如果需要特殊的提取逻辑:
```vba
' 在 clsConditionExtractor.ExtractValue 方法中添加:
Case "GetMiddleChars"
' 提取中间几位字符
If Len(sourceValue) > 4 Then
ExtractValue = Mid(sourceValue, 2, 3)
Else
ExtractValue = sourceValue
End If
```
### 扩展条件匹配运算符
`clsConditionMatcher.EvaluateSimpleCondition` 中添加新运算符:
```vba
' 示例: 添加 > 运算符
ElseIf InStr(cond, ">") > 0 Then
' ... 实现大于比较
```
---
## 常见问题
### Q1: 型号解析失败怎么办?
**A**: 检查型号格式是否正确:
- 表头部分必须包含: `型号-外径.安装.壳体.连接.量程.[特性]`
- 各部分用 `.` 分隔
- 表头和表盘用 `|` 分隔
### Q2: 条件匹配不准确?
**A**:
1. 检查物料的选择条件表达式是否正确
2. 运行 `Test3_ConditionMatcher` 验证匹配器功能
3. 使用 `TestExpression` 方法测试具体表达式
### Q3: 如何调试条件提取?
**A**: 使用以下代码查看提取结果:
```vba
Dim conditions As Object
Set conditions = bomMgr.ParseModelAndExtractConditions(modelStr)
Dim key As Variant
For Each key In conditions.Keys
Debug.Print key & " = " & conditions(key)
Next key
```
### Q4: 批量处理速度慢?
**A**:
1. 避免在循环中重复加载BOM数据
2. 使用 `GetMaterialsByModel` 一次性获取所有类别
3. 考虑添加缓存机制
### Q5: 如何验证物料条件表达式?
**A**: 运行示例:
```vba
Example5_ValidateMaterialConditions
```
这将生成一个验证报告,显示所有物料条件的有效性。
---
## 测试用例
### 运行所有测试
```vba
RunAllTests
```
### 单独测试
```vba
' 测试型号解析
Test1_ModelParser
' 测试条件提取
Test2_ConditionExtractor
' 测试条件匹配
Test3_ConditionMatcher
' 测试完整流程
Test4_GetMaterialsByModel
```
---
## 应用示例
### 示例1: 生成领料清单
```vba
Example1_GeneratePickingListByModel
```
### 示例2: 批量处理型号
```vba
Example2_BatchProcessModels
```
### 示例3: 查询型号详情
```vba
Example3_ShowModelDetails
```
### 示例4: 对比两个型号
```vba
Example4_CompareModels
```
### 示例5: 验证条件表达式
```vba
Example5_ValidateMaterialConditions
```
---
## 技术支持
如有问题,请:
1. 查看测试结果和调试信息
2. 运行相关示例程序
3. 检查条件表达式语法
---
**文档版本**: v1.0
**最后更新**: 2026-01-19

View File

@@ -1,635 +0,0 @@
# 订单物料提取功能实现计划
## 一、项目概述
### 1.1 目标
实现从产品型号中提取选择条件,并根据条件匹配 BOM 库中对应的物料,生成订单物料清单。
### 1.2 核心功能
1. **型号解析**:从产品型号中提取选择条件变量
2. **条件匹配引擎**:解析并匹配物料的选择条件表达式
3. **物料清单生成**:整合上述功能,生成符合产品配置的物料清单
---
## 二、产品型号编码规则
### 2.1 型号结构
```
[型号] - [公称外径] . [安装形式] . [壳体形式] . [过程连接&接液材质] . [量程范围] . [仪表特性] | [表盘] | [附件] | [法兰隔膜]
```
### 2.2 示例分析
**输入**: `YTHN-100.A0.532.M203.M16.Y3|BP-095.2312.M16.PA3`
**表头部分**: `YTHN-100.A0.532.M203.M16.Y3`
| 部位 | 代码 | 含义 |
|------|------|------|
| 型号 | YTHN | 不锈钢耐震压力表 |
| 公称外径 | 100 | 100mm |
| 安装形式 | A0 | 径向无边 |
| 壳体形式 | 532 | 532系列(304外壳、内卡式) |
| 过程连接&接液材质 | M203 | M20螺纹 + 3号材质(304) |
| 量程范围 | M16 | 060 MPa |
| 仪表特性 | Y3 | 硅油C充油 |
---
## 三、需求分析
### 3.1 提取的选择条件
| 变量名 | 变量标签 | 提取位置 | 提取规则 |
|--------|----------|----------|----------|
| gclj | 过程连接 | 过程连接&接液材质 | 除最后一位外的字符 |
| jycz | 接液材质 | 过程连接&接液材质 | 最后一位数字 |
| lcfw | 量程范围 | 量程范围 | 完整代码 |
### 3.2 提取示例
**输入**: `M203`
- 过程连接(gclj): `M20`
- 接液材质(jycz): `3`
**输入**: `M16`
- 量程范围(lcfw): `M16`
### 3.3 匹配逻辑
#### 物料选择条件类型
1. **无条件**:条件为空,表示该物料适用于所有配置
2. **简单条件**:单个条件判断
- 例: `lcfw=M01`
3. **AND 条件**:多个条件同时满足
- 例: `lcfw=M02 AND gclj=M20`
4. **OR 条件**:任一条件满足即可
- 例: `lcfw=M01 OR lcfw=M02 OR lcfw=M03`
5. **NOT 条件**:排除特定条件
- 例: `lcfw=M02 AND gclj!=M20`
6. **复杂条件**AND、OR、NOT 混合
- 例: `gclj=Z12 AND (lcfw=M01 OR lcfw=M02 OR lcfw=M03)`
---
## 四、技术架构设计
### 4.1 模块结构
```
VBA/
├── ClassModules/
│ ├── clsBOMManager.cls # (已有)BOM管理器
│ ├── clsCategory.cls # (已有)类别类
│ ├── clsMaterialItem.cls # (已有)物料项类
│ ├── clsModelParser.cls # (新增)型号解析器
│ ├── clsConditionEvaluator.cls # (新增)条件表达式求值器
│ └── clsOrderMaterialList.cls # (新增)订单物料清单管理器
└── Modules/
└── modOrderProcessor.bas # (新增)订单处理模块
```
### 4.2 类设计
#### 4.2.1 clsModelParser型号解析器
**职责**: 解析产品型号,提取选择条件
**属性**:
```vba
Public ModelCode As String ' 完整型号代码
Public HeaderPart As String ' 表头部分
Public DialPart As String ' 表盘部分
Public AccessoryPart As String ' 附件部分
Public FlangePart As String ' 法兰隔膜部分
```
**方法**:
```vba
' 解析型号
Public Sub ParseModel(modelCode As String)
' 提取表头部分
Public Function ExtractHeader() As String
' 提取过程连接代码
Public Function GetProcessConnection() As String
' 提取接液材质代码
Public Function GetWettedMaterial() As String
' 提取量程范围代码
Public Function GetRangeCode() As String
' 获取所有选择条件
Public Function GetSelectionConditions() As Object
```
**示例数据结构**:
```vba
' 返回字典对象
' Key: 变量名 (如 "gclj", "jycz", "lcfw")
' Value: 变量值 (如 "M20", "3", "M16")
```
---
#### 4.2.2 clsConditionEvaluator条件表达式求值器
**职责**: 解析和求值物料选择条件表达式
**方法**:
```vba
' 求值条件表达式
' 参数:
' conditionExpr - 条件表达式 (如 "lcfw=M02 AND gclj=M20")
' conditionsDict - 从型号提取的条件字典
' 返回: Boolean - 是否匹配
Public Function Evaluate(conditionExpr As String, _
conditionsDict As Object) As Boolean
' 解析条件表达式为抽象语法树(AST)
Private Function ParseExpression(expr As String) As Object
' 求值单个条件
' 例: "lcfw=M02" -> True/False
Private Function EvaluateSingleCondition(condition As String, _
conditionsDict As Object) As Boolean
' 求值 AND 表达式
Private Function EvaluateAndExpression(conditions As Collection, _
conditionsDict As Object) As Boolean
' 求值 OR 表达式
Private Function EvaluateOrExpression(conditions As Collection, _
conditionsDict As Object) As Boolean
' 求值 NOT 表达式
Private Function EvaluateNotExpression(condition As String, _
conditionsDict As Object) As Boolean
```
**支持的运算符**:
- `=` : 等于
- `!=``<>` : 不等于
- `AND` : 逻辑与
- `OR` : 逻辑或
- `()` : 分组优先级
---
#### 4.2.3 clsOrderMaterialList订单物料清单管理器
**职责**: 管理单个订单的物料清单
**属性**:
```vba
Public OrderModel As String ' 订单型号
Public OrderConditions As Object ' 订单选择条件字典
Public SelectedMaterials As Collection ' 匹配的物料集合
```
**方法**:
```vba
' 根据订单型号生成物料清单
Public Sub GenerateMaterialList(modelCode As String, _
bomMgr As clsBOMManager)
' 筛选符合条件的物料
' 参数:
' categoryName - 类别名称
' bomMgr - BOM管理器
Private Function FilterMaterialsByCategory(categoryName As String, _
bomMgr As clsBOMManager) As Collection
' 获取物料汇总信息
Public Function GetMaterialSummary() As String
' 导出物料清单到工作表
Public Sub ExportToWorksheet(ws As Worksheet)
' 打印物料清单到立即窗口
Public Sub PrintToImmediateWindow()
```
---
#### 4.2.4 clsBOMManager 扩展
**新增方法**:
```vba
' 根据选择条件获取物料清单
' 参数:
' categoryName - 类别名称
' conditionsDict - 选择条件字典
' 返回: 匹配的物料集合
Public Function GetMaterialsByConditions(categoryName As String, _
conditionsDict As Object) As Collection
' 获取整个订单的物料清单
' 参数:
' conditionsDict - 从型号提取的选择条件字典
' useParent - 是否使用父类别领料方式
' 返回: 所有类别的匹配物料汇总
Public Function GetOrderMaterials(conditionsDict As Object, _
Optional useParent As Boolean = True) As Collection
```
---
## 五、实现步骤
### 阶段一型号解析器clsModelParser
**优先级**: 高
**任务清单**:
1. 创建 `clsModelParser.cls` 类模块
2. 实现型号分段解析(表头、表盘、附件、法兰隔膜)
3. 实现表头部分的详细解析
4. 实现选择条件提取方法
5. 编写单元测试用例
**测试用例**:
```vba
Sub TestModelParser()
Dim parser As New clsModelParser
Dim conditions As Object
' 测试1: 标准型号
parser.ParseModel "YTHN-100.A0.532.M203.M16.Y3|BP-095.2312.M16.PA3"
Set conditions = parser.GetSelectionConditions()
Debug.Assert conditions("gclj") = "M20"
Debug.Assert conditions("jycz") = "3"
Debug.Assert conditions("lcfw") = "M16"
' 测试2: 不同过程连接
parser.ParseModel "YTHN-100.A0.532.Z121.M06.Y3"
Set conditions = parser.GetSelectionConditions()
Debug.Assert conditions("gclj") = "Z12"
Debug.Assert conditions("jycz") = "1"
Debug.Assert conditions("lcfw") = "M06"
End Sub
```
---
### 阶段二条件表达式求值器clsConditionEvaluator
**优先级**: 高
**任务清单**:
1. 创建 `clsConditionEvaluator.cls` 类模块
2. 实现条件表达式词法分析(分词)
3. 实现条件表达式语法分析(构建 AST
4. 实现各种运算符的求值逻辑
5. 处理括号优先级
6. 编写单元测试用例
**测试用例**:
```vba
Sub TestConditionEvaluator()
Dim evaluator As New clsConditionEvaluator
Dim conditions As Object
Set conditions = CreateObject("Scripting.Dictionary")
conditions("gclj") = "M20"
conditions("jycz") = "3"
conditions("lcfw") = "M16"
' 测试1: 简单条件
Debug.Assert evaluator.Evaluate("lcfw=M16", conditions) = True
Debug.Assert evaluator.Evaluate("lcfw=M02", conditions) = False
' 测试2: AND 条件
Debug.Assert evaluator.Evaluate("lcfw=M16 AND gclj=M20", conditions) = True
Debug.Assert evaluator.Evaluate("lcfw=M02 AND gclj=M20", conditions) = False
' 测试3: OR 条件
Debug.Assert evaluator.Evaluate("lcfw=M16 OR lcfw=M02", conditions) = True
Debug.Assert evaluator.Evaluate("lcfw=M01 OR lcfw=M02", conditions) = False
' 测试4: NOT 条件
Debug.Assert evaluator.Evaluate("lcfw=M16 AND gclj!=M20", conditions) = False
Debug.Assert evaluator.Evaluate("lcfw=M16 AND gclj!=Z12", conditions) = True
' 测试5: 复杂条件
Debug.Assert evaluator.Evaluate("gclj=Z12 AND (lcfw=M01 OR lcfw=M02 OR lcfw=M16)", conditions) = False
' 测试6: 空条件
Debug.Assert evaluator.Evaluate("", conditions) = True
End Sub
```
---
### 阶段三BOM管理器扩展
**优先级**: 中
**任务清单**:
1.`clsBOMManager.cls` 中添加 `GetMaterialsByConditions()` 方法
2.`clsBOMManager.cls` 中添加 `GetOrderMaterials()` 方法
3. 集成条件求值器
4. 编写单元测试用例
**测试用例**:
```vba
Sub TestBOMManagerConditions()
Dim bomMgr As New clsBOMManager
Dim conditions As Object
Dim materials As Collection
' 加载数据
bomMgr.LoadData _
ThisWorkbook.Worksheets("领料配置"), _
ThisWorkbook.Worksheets("平台配置清单")
' 准备条件
Set conditions = CreateObject("Scripting.Dictionary")
conditions("gclj") = "M20"
conditions("lcfw") = "M16"
' 测试: 获取部件类别的匹配物料
Set materials = bomMgr.GetMaterialsByConditions("部件", conditions)
' 验证结果
Debug.Print "匹配物料数量: " & materials.Count
' 应该只有 01011019018 (高压接头部件) 匹配
Dim mat As clsMaterialItem
For Each mat In materials
Debug.Print mat.code & " - " & mat.Name
Next mat
End Sub
```
---
### 阶段四订单物料清单管理器clsOrderMaterialList
**优先级**: 中
**任务清单**:
1. 创建 `clsOrderMaterialList.cls` 类模块
2. 实现物料清单生成逻辑
3. 实现物料汇总统计
4. 实现多种输出格式(工作表、立即窗口)
5. 编写单元测试用例
**测试用例**:
```vba
Sub TestOrderMaterialList()
Dim orderList As New clsOrderMaterialList
Dim bomMgr As New clsBOMManager
' 加载 BOM 数据
bomMgr.LoadData _
ThisWorkbook.Worksheets("领料配置"), _
ThisWorkbook.Worksheets("平台配置清单")
' 生成物料清单
orderList.GenerateMaterialList "YTHN-100.A0.532.M203.M16.Y3", bomMgr
' 打印到立即窗口
orderList.PrintToImmediateWindow
' 导出到工作表
Dim ws As Worksheet
Set ws = ThisWorkbook.Worksheets.Add
orderList.ExportToWorksheet ws
End Sub
```
---
### 阶段五订单处理模块modOrderProcessor
**优先级**: 低
**任务清单**:
1. 创建 `modOrderProcessor.bas` 模块
2. 实现批量订单处理过程
3. 实现单订单物料清单生成
4. 实现订单物料清单打印和导出
5. 编写用户界面宏
**示例宏**:
```vba
' 主入口:生成订单物料清单
Sub GenerateOrderMaterialList()
Dim modelCode As String
modelCode = InputBox("请输入产品型号:", "生成订单物料清单")
If modelCode = "" Then Exit Sub
Dim orderList As New clsOrderMaterialList
Dim bomMgr As New clsBOMManager
bomMgr.LoadData _
ThisWorkbook.Worksheets("领料配置"), _
ThisWorkbook.Worksheets("平台配置清单")
orderList.GenerateMaterialList modelCode, bomMgr
' 输出到工作表
Dim ws As Worksheet
Application.DisplayAlerts = False
On Error Resume Next
ThisWorkbook.Worksheets("订单物料清单").Delete
On Error GoTo 0
Application.DisplayAlerts = True
Set ws = ThisWorkbook.Worksheets.Add
ws.Name = "订单物料清单"
orderList.ExportToWorksheet ws
MsgBox "订单物料清单生成完成!", vbInformation
End Sub
' 快速打印到立即窗口
Sub QuickPrintOrderMaterials()
Dim modelCode As String
modelCode = InputBox("请输入产品型号:", "快速打印订单物料")
If modelCode = "" Then Exit Sub
' ... 实现逻辑
End Sub
```
---
## 六、数据流程图
```
用户输入产品型号
[clsModelParser] 解析型号
提取选择条件字典 {gclj: M20, jycz: 3, lcfw: M16}
[clsOrderMaterialList] 生成物料清单
遍历所有根类别
对每个类别调用 [clsBOMManager.GetMaterialsByConditions()]
遍历类别下的所有物料
对每个物料使用 [clsConditionEvaluator.Evaluate()]
匹配成功 → 添加到结果集合
匹配失败 → 跳过
汇总所有匹配物料
输出到工作表 / 立即窗口
```
---
## 七、关键技术难点
### 7.1 条件表达式解析
**难点**:
- 支持括号嵌套
- 运算符优先级处理
- 复杂逻辑组合
**解决方案**:
- 使用递归下降分析法
- 构建抽象语法树(AST)
- 后序遍历求值
### 7.2 型号格式多样性
**难点**:
- 部分型号可能缺少某些部分
- 附件和法兰隔膜数量不固定
- 过程连接代码长度不固定
**解决方案**:
- 使用正则表达式(如果 VBA 支持)
- 分段解析,容错处理
- 提供默认值
### 7.3 性能优化
**难点**:
- 大量物料匹配可能导致性能问题
- 复杂条件表达式求值耗时
**解决方案**:
- 缓存求值结果
- 优先筛选简单条件
- 减少不必要的条件求值
---
## 八、测试计划
### 8.1 单元测试
每个类都需要独立的单元测试模块:
1. **TestModelParser**: 测试型号解析功能
2. **TestConditionEvaluator**: 测试条件求值功能
3. **TestBOMManagerConditions**: 测试 BOM 管理器扩展功能
4. **TestOrderMaterialList**: 测试订单物料清单生成功能
### 8.2 集成测试
测试完整的订单物料生成流程:
1. 测试标准型号
2. 测试特殊配置型号
3. 测试边界条件
4. 测试异常输入
### 8.3 测试数据集
基于真实订单数据准备测试用例:
| 测试用例 | 型号 | 预期匹配物料数 |
|----------|------|----------------|
| 标准配置 | YTHN-100.A0.532.M203.M16.Y3 | 待定 |
| 不同螺纹 | YTHN-100.A0.532.Z121.M06.Y3 | 待定 |
| 小量程 | YTHN-100.A0.532.M201.M02.Y3 | 待定 |
| 大量程 | YTHN-100.A0.532.M201.M16.Y3 | 待定 |
---
## 九、文档需求
### 9.1 代码注释
- 所有类模块需要详细的中文注释
- 关键算法需要解释说明
- 复杂逻辑需要举例说明
### 9.2 使用说明
创建 `订单物料清单使用说明.md`,包括:
- 功能概述
- 使用方法
- API 文档
- 常见问题
### 9.3 开发文档
- 类设计文档
- 接口定义文档
- 测试报告
---
## 十、实施时间表
| 阶段 | 任务 | 预计工作量 |
|------|------|------------|
| 1 | 型号解析器 | 2-3 天 |
| 2 | 条件表达式求值器 | 3-4 天 |
| 3 | BOM管理器扩展 | 1-2 天 |
| 4 | 订单物料清单管理器 | 2-3 天 |
| 5 | 订单处理模块 | 1-2 天 |
| 6 | 测试与调试 | 2-3 天 |
| 7 | 文档编写 | 1-2 天 |
**总计**: 约 12-19 天
---
## 十一、风险评估
### 11.1 技术风险
- **风险**: VBA 不支持复杂的字符串处理
- **应对**: 使用 VBScript Regular Expression 对象
### 11.2 数据风险
- **风险**: BOM 数据不完整或格式错误
- **应对**: 增加数据验证和容错处理
### 11.3 性能风险
- **风险**: 大量物料匹配导致响应缓慢
- **应对**: 优化算法,增加缓存机制
---
## 十二、后续扩展方向
1. **批量订单处理**: 支持从 Excel 列表批量生成物料清单
2. **条件配置化**: 将提取规则配置化,支持动态调整
3. **物料替代方案**: 支持物料替代品推荐
4. **库存集成**: 与库存系统集成,实时查询可用库存
5. **成本计算**: 根据物料清单计算订单成本
---
## 附录:参考资料
- 布莱迪公司压力表产品选型大表.md
- clsBOMManager使用说明.md
- VBA 编程规范
---
**文档版本**: 1.0
**创建日期**: 2025-01-20
**编制人**: Claude Code
**审核状态**: 待审核