返回作品列表

网友作品 #000052 · 提交于 2026/08/06

造价工具箱:Excel材料清单模糊匹配回填上价工具

★★★★★零基础pythondotnet

作品作者

张三零基础
使用 Skill
pythondotnet
提交你的作品

作品简介

造价工具箱 V0.3 是一款面向工程造价场景的 Excel 数据处理桌面工具,解决的核心问题是:目标表(A 表)与源数据表(B 表)文本写法不一致时,如何把 B 表的材料、综合单价等信息准确找回来填进 A 表,替代人工逐条检索核对。

工具提供两大功能模块:模糊匹配回填按多列加权相似度匹配两表数据,把 B 表指定列回填到 A 表;特征合并把多列特征数据拼接为带序号的格式化文本输出到指定列。

界面按 MVVM 架构组织,功能页一键切换,匹配规则通过弹窗可视化配置,发布为自包含运行包后拷贝到任何 Windows 电脑即可使用。

核心亮点

  • 多列加权模糊匹配:基于 Levenshtein 编辑距离计算列相似度并加权平均,权重 0-100 步进 5、总和须为 100
  • 规则全可视化配置:表头行号、A/B 表匹配列、权重映射、触发列、结果列、回填映射均可在设置弹窗中完成
  • 点击即匹配:在 A 表触发列点击单元格即发起该行模糊匹配,结果弹窗展示 TopN 候选,确认后回填
  • 溯源与批量:匹配结果所在的 B 表行号写入结果列便于核对溯源;支持配置「一同回填条件」,同条件行一键同回填
  • 特征合并:勾选参与列、设置单位后,逐行拼接为「1、名称:×× 2、高度:××」式带序号文本,批量写入目标列
  • 防御性读写:写入前检测文件是否被 Excel 占用,自动解除目标单元格合并区域,裁剪尾部空行,单表最大 10 万行
  • 免安装运行:自包含发布为独立文件夹,拷贝即用,无需安装 .NET 运行时

技术与工具

  • .NET 9 + WPF 桌面端,界面采用 HandyControl 控件库
  • Excel 读写基于 EPPlus 8.6(.xlsx),读写均记录 Excel 真实行号避免表头偏移
  • CommunityToolkit.Mvvm 源生成器支撑数据绑定,Serilog 按日期滚动记录日志
  • 配置以 JSON 持久化在用户应用数据目录,含匹配配置、合并配置与最近文件
  • 作者零基础,耗时约一周完成开发

开发过程

  • 开发中沉淀了多条避坑经验:WPF 单文件发布与 HandyControl 主题资源不兼容会导致启动静默退出,改用多文件自包含发布解决
  • 切换工作表后权重映射的列名字典未同步刷新会引发负索引崩溃,通过同步更新字典引用并过滤负数索引修复
  • Excel 列名含括号、点号等特殊字符会导致 WPF 绑定失败,采用特殊字符替换加标题恢复的方案处理

适用场景

  • 工程造价:工程清单中材料、综合单价的批量模糊匹配上价,减少人工检索核对
  • 多源数据整合:不同 Excel 表格间非标准化文本的匹配关联与信息回填,如库存盘点、客户信息对账
原始提交内容(点开查看,可复制全文去问 AI)

造价工具箱 V0.3

基于 .NET 9 + WPF + EPPlus 的造价数据处理工具,支持模糊匹配回填与特征合并。


一、功能概览

功能 说明
模糊匹配回填 A表(目标)与B表(源数据)按多列加权模糊匹配,将B表数据回填到A表
特征合并 将多列特征数据合并为带序号的格式化文本,输出到指定列

二、技术栈

技术 版本 用途
.NET 9.0 运行时框架
WPF — 桌面UI
EPPlus 8.6.0 Excel读写(.xlsx)
HandyControl 3.5.1 WPF UI控件库
CommunityToolkit.Mvvm 8.4.2 MVVM支持(源生成器)
Serilog 4.3.1 日志记录

三、项目结构

src/CostToolbox/
 App.xaml                  # 应用入口,HandyControl主题配置
 CostToolbox.csproj        # 项目文件
 Models/                   # 数据模型(纯POCO)
    BackfillMapping.cs    # 回填映射(A列B列)
    CellWrite.cs          # 单元格写入指令
    ColumnWeightPair.cs   # 列权重对(A列B列)
    FeatureMergeConfig.cs # 特征合并配置
    MatchConfig.cs        # 模糊匹配配置
    MatchGroup.cs         # 匹配组(列选择+权重)
    MatchResult.cs        # 单条匹配结果
    SheetData.cs          # 工作表数据(含Excel真实行号)
 Services/                 # 业务服务层
    ExcelService.cs       # Excel读写(EPPlus封装)
    FuzzyMatchService.cs  # 模糊匹配核心(Levenshtein)
    FeatureMergeService.cs# 特征合并核心
    ConfigService.cs      # JSON配置持久化
    LogService.cs         # Serilog日志初始化
    I*.cs                 # 接口定义
 ViewModels/               # MVVM视图模型
    FuzzyMatchViewModel.cs# 模糊匹配页面VM
    FeatureMergeViewModel.cs# 特征合并页面VM
    MainViewModel.cs      # 主窗口VM
 Views/                    # WPF视图
    MainWindow.xaml       # 主窗口(双视图切换)
    MatchSettingDialog.xaml # 匹配设置弹窗
    MatchResultDialog.xaml  # 匹配结果弹窗
    SaveConfirmDialog.xaml  # 保存确认弹窗
 Converters/               # 值转换器

四、核心逻辑详解

4.1 模糊匹配回填

匹配算法:Levenshtein编辑距离

// FuzzyMatchService.cs
public double LevenshteinSimilarity(string? a, string? b)
{
    // 动态规划计算编辑距离
    // 相似度 = 1 - distance / max(lenA, lenB)
    return 1.0 - (double)distance / Math.Max(m, n);
}

核心流程:

  1. 配置匹配规则(MatchSettingDialog)

    • 选择A表、B表的匹配列(可多选)
    • 配置列权重映射(如:A表“名称”B表“项目名称”,权重40)
    • 设置触发列(点击A表该列即触发匹配)
    • 设置回填映射(将B表的哪些列回填到A表的哪些列)
  2. 加权匹配计算

    // 对每对ColumnWeights计算加权平均相似度
    double avgScore = totalWeightedSim / totalWeight;
  3. 回填写入

    • 匹配结果弹窗展示TopN候选
    • 用户确认后,将B表指定列数据写入A表
    • 同时写入B表行号到结果列,方便溯源

关键设计:Excel真实行号映射

// SheetData.cs
public class SheetData
{
    public List<List<string>> Rows { get; }        // 0-based索引数据
    public List<int> ExcelRowNumbers { get; }      // 对应的真实Excel行号(1-based)
}

_fullDataA.Rows[i] 是内存中的0-based索引,而 _fullDataA.ExcelRowNumbers[i] 是对应的Excel真实行号。所有回填操作都基于真实行号,避免表头行偏移错误。

4.2 特征合并

// FeatureMergeService.cs
public string MergeRow(List<string> row, List<string> headers, 
    List<int> paramCols, Dictionary<int, string> units, ...)

输出格式:

1、名称:丛生香樟
2、高度:750-850
3、冠幅:550-600
4、备注:丛生杆数5杆以上
5、其他未尽事宜,具体详见图纸。

流程:

  1. 勾选参与合并的列
  2. 为各列设置单位(可选,留空不追加)
  3. 指定输出目标列(如K列)
  4. 逐行拼接生成带序号文本,批量写入Excel

4.3 Excel读写服务(EPPlus 8)

// ExcelService.cs 核心方法
Task<ExcelFileInfo> OpenAsync(string filePath);
Task<SheetData> ReadAllRowsAsync(string filePath, string sheetName, int headerRow);
Task BatchBackfillAsync(string filePath, string sheetName, IEnumerable<CellWrite> writes);

防御性设计:

  • 文件占用检测:任何写入前先以 FileShare.ReadWrite 尝试打开,被Excel占用则立即抛异常
  • 合并单元格处理:写入前自动解除目标单元格所在合并区域
  • 尾部空行裁剪:读取时从 Dimension.Rows 往回扫描,跳过尾部连续空行,避免格式残留导致卡死
  • 行数上限:ReadAllRowsAsync 限制最大10万行

五、MVVM架构

5.1 数据绑定链

View (XAML)  ViewModel (CommunityToolkit.Mvvm)  Service  Model

5.2 关键特性

  • [ObservableProperty] 源生成器:编译期自动生成属性变更通知代码,无需手写 INotifyPropertyChanged 实现
  • [RelayCommand] 源生成器:自动将方法包装为 ICommand,支持异步命令
  • Partial方法属性变更钩子:如 OnSelectedSheetAChanged 在属性变化时自动触发加载

5.3 视图切换

MainWindow.xaml 中使用两个 Grid(FuzzyMatchContent / FeatureMergeContent)通过 Visibility 切换,共享同一个 DataContext 的不同子ViewModel:

FuzzyMatchPanel.DataContext = _fuzzyMatchVm;
FeatureMergePanel.DataContext = _featureMergeVm;

六、避坑指南

6.1 WPF单文件发布与HandyControl不兼容

问题:PublishSingleFile=true 时,HandyControl的 pack://application:,,,/HandyControl;component/Themes/SkinDefault.xaml 资源加载失败,程序启动后静默退出。

解决:取消单文件发布,使用多文件自包含发布。

6.2 权重映射行字典未同步更新

问题:切换工作表/表头行后,WeightMappingRow 内部的 _mapA/_mapB(列名索引字典)未同步刷新,导致返回 -1 索引,保存后匹配时 rowA[-1] 崩溃。

解决:RefreshColsA/B 方法中同步更新字典引用,保存前过滤负数索引。

6.3 DataGrid列名含特殊字符导致绑定失败

问题:Excel列名含 ()、[]、. 等特殊字符时,WPF PropertyPath 解析失败。

解决:SanitizeForBinding 方法将特殊字符替换为全角等价字符,同时用 DataColumn.Caption 保存原始标题,在 AutoGeneratingColumn 事件中恢复显示。

6.4 另一台电脑加载B表卡死

排查顺序:

  1. 确认B表未被Excel占用
  2. 确认文件为 .xlsx 格式
  3. 检查是否缺少VC++运行库
  4. 检查文件是否含大量空行/格式行(已做尾部空行裁剪优化)

七、构建与发布

7.1 开发环境要求

  • .NET 9 SDK
  • Visual Studio 2022 或 VS Code + C# Dev Kit
  • Windows 10/11

7.2 本地运行

cd src/CostToolbox
dotnet run

7.3 发布为独立运行包

dotnet publish -c Release --self-contained true -r win-x64 -o ./发布

发布后的 发布 文件夹可直接拷贝到任何Windows电脑运行,无需安装.NET运行时。


八、配置存储

配置以JSON格式存储在用户应用数据目录:

%LocalAppData%/CostToolbox/config.json

包含:

  • MatchConfig:模糊匹配配置(表头行、匹配列、权重、回填映射等)
  • FeatureMergeConfig:特征合并配置
  • RecentFile:最近打开的文件路径

九、日志

日志文件位于程序同目录 logs/ 下,按日期滚动:

logs/costtoolbox-20260613.log

使用Serilog File Sink,记录所有操作和异常堆栈,便于排查问题。


十、后续开发约定

已稳定的两个核心功能模块,后续新增功能时严禁改动:

  1. 模糊匹配回填:FuzzyMatchService、FuzzyMatchViewModel、MatchSettingDialog
  2. 特征合并:FeatureMergeService、FeatureMergeViewModel、ExcelService 读写接口

新增功能应独立建类/建页,避免影响已有稳定逻辑。


本作品归公众号 Excel催化剂 所有,仅限授权用户自用。