AIGC标识 WinForms + SQLite 实战:给工厂做一套报工系统,替掉 3 本纸质台账(10 天交付、8 个月零维护)

一、需求不是「上系统」,而是「把纸换成数据」

去年给一家做五金件的小厂看现场,车间的现状是这样的:质量一本台账、产量一本台账、异常一本台账。早班工人手写报工,午休时班长核一遍,晚上文员再逐页敲进 Excel。三本账对不上的那天,全车间一起加班找数据。

老板的诉求其实非常朴素:

  • 不改变工人的操作习惯(原话是「老张五十多了,你让他学新东西他不会」)
  • 月底能直接拉出报表,不用再对三本纸账
  • 别搞服务器、别搞一堆部署步骤,车间那台老电脑直接就能跑

这三条约束基本就定死了技术选型。

二、为什么选 WinForms 而不是 WPF

很多人第一反应是「2026 年了还 WinForms?」,但在这个场景里 WinForms 是对的:

约束一:客户电脑是老 Win7 一体机。WinForms 对老系统的兼容性和启动速度,比 WPF 有优势(没有复杂的硬件加速依赖,老显卡不会出现渲染异常)。

约束二:界面极其简单。整个系统只有 3 个界面,最大的界面就是几个大按钮加一个输入框。WPF 的 MVVM、样式体系、数据绑定在这里是纯粹的过度设计,反而增加维护成本。

约束三:要免安装。WinForms 单文件发布后是一个 exe,双击就跑;不需要 .NET 运行时的话可以自包含发布,体积代价可接受。

如果这个项目的界面复杂、有大量动画或者需要皮肤切换,我会选 WPF。但这里是「一个大按钮 + 一张表」,选 WPF 是给自己找活。

三、四个关键实现

3.1 扫码枪:它本质上就是一个键盘

很多人以为接扫码枪要写驱动或者串口通讯,其实不是。市面上的 USB 扫码枪绝大多数工作在 HID 键盘模式——它把扫到的条码当作一串键盘按键「打」进当前焦点窗口,末尾自动补一个回车。

所以服务端只需要在窗体级别处理键盘事件,靠输入间隔把扫码和人工手打区分开:

private readonly StringBuilder _barcode = new();
private DateTime _lastKeyTime = DateTime.Now;

private void FormMain_KeyPress(object sender, KeyPressEventArgs e)
{
    // 扫码枪是键盘仿真设备:极短间隔连续输入,末尾一个回车
    // 人工手打间隔通常 > 300ms,以此区分
    if ((DateTime.Now - _lastKeyTime).TotalMilliseconds > 300)
        _barcode.Clear();

    _lastKeyTime = DateTime.Now;

    if (e.KeyChar == '\r')              // 扫码结束
    {
        var code = _barcode.ToString().Trim();
        _barcode.Clear();
        if (code.Length > 0) SubmitReport(code);
        e.Handled = true;
        return;
    }

    if (!char.IsControl(e.KeyChar)) _barcode.Append(e.KeyChar);
}

两个容易踩的坑:

  1. 焦点丢失。工人点了别的地方,扫码就打到别的控件上去了。解决办法是把 KeyPreview = true,在窗体层拦截,并且在 LostFocus 时自动把焦点抢回来。
  2. 输入法。如果当前是中文输入法,条码会被输入法吞掉。上线前一定要在机器上把输入法设为英文,或者程序启动时调 ImmAssociateContext 禁用输入法。

3.2 断网可用:SQLite 本地库

车间网络抽风是常态,所以数据必须先落本地。SQLite + Microsoft.Data.Sqlite 就够,日产量几百条这个量级根本不需要考虑 WAL 和并发优化(单用户单进程,写入压力可以忽略)。

private const string ConnString = "Data Source=report.db;Cache=Shared";

public static void InitDb()
{
    using var conn = new SqliteConnection(ConnString);
    conn.Open();
    using var cmd = conn.CreateCommand();
    cmd.CommandText = @"
        CREATE TABLE IF NOT EXISTS ReportRecord(
            Id        INTEGER PRIMARY KEY AUTOINCREMENT,
            WorkOrder TEXT    NOT NULL,
            Station   TEXT    NOT NULL,
            Qty       INTEGER NOT NULL,
            CreatedAt TEXT    NOT NULL      -- ISO8601,方便按天/按周分组
        );
        CREATE INDEX IF NOT EXISTS idx_report_created ON ReportRecord(CreatedAt);";
    cmd.ExecuteNonQuery();
}

时间统一存 ISO8601 字符串(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")),SQLite 的 date(CreatedAt) 直接就能按天聚合,省掉一层转换。

3.3 报表导出:ClosedXML,不依赖客户装没装 Office

这是踩过的坑:最早用 COM 调 Excel 导出,客户机器上是 WPS,直接报错;换了 Microsoft.Office.Interop.Excel 也是一样的下场,而且还会残留后台进程。

最后换成 ClosedXML(纯托管库,写 .xlsx 不需要 Office):

public static void ExportDaily(string path, DateTime day)
{
    var rows = QueryDaily(day);                  // List<(string Station, string Order, int Qty)>

    using var wb = new XLWorkbook();
    var ws = wb.AddWorksheet("产量日报");

    ws.Cell(1, 1).Value = "工位";
    ws.Cell(1, 2).Value = "工单号";
    ws.Cell(1, 3).Value = "数量";
    ws.Row(1).Style.Font.Bold = true;

    for (int i = 0; i < rows.Count; i++)
    {
        ws.Cell(i + 2, 1).Value = rows[i].Station;
        ws.Cell(i + 2, 2).Value = rows[i].Order;
        ws.Cell(i + 2, 3).Value = rows[i].Qty;
    }

    ws.Columns().AdjustToContents();
    ws.RangeUsed()?.SetAutoFilter();             // 文员拿到就能直接筛
    wb.SaveAs(path);
}

纸质报表则用 PrintDocument 直接打,不经过 Excel 这条链路,稳定得多。

3.4 免安装部署:一行注册表搞定开机自启

车间电脑不需要「启动软件」这个动作,开机就该在跑:

using var key = Registry.CurrentUser.OpenSubKey(
    @"Software\Microsoft\Windows\CurrentVersion\Run", writable: true);
key?.SetValue("WorkshopReport", Application.ExecutablePath);

部署方式:整个目录拷到车间电脑 D:\WorkshopReport\,双击一次 exe(首次会写注册表),之后开机自动运行。不做安装包——安装包在老电脑上反而容易出问题(权限、杀软拦截)。

四、结果

上线 8 个多月的数据:

指标 改造前 改造后
台账本数 3 本纸质 0
日汇总耗时 40 分钟/天 自动生成
月底对账 2 天 10 分钟
交付周期 10 个工作日
现场维护次数 0

工人侧的变化几乎为零:选工位 → 扫工单 → 点「报工」,三步。年纪最大的师傅大概 5 分钟上手。

五、什么时候不该这么做

这套方案有明确的适用边界,我不想把它说成万能药:

  • 日产量上万条、多车间多班次联动:该上 MES,本地 SQLite 撑不住这种协同复杂度
  • 需要和 ERP、财务系统实时打通:那必须有服务端,单机库做不了数据同步
  • 界面复杂、有大量实时图表:WPF 或 Web 更合适

判断标准很简单:如果「数据只在一个车间内流转、汇总颗粒度是天」,那 WinForms + SQLite 的性价比远高于任何 B/S 方案——没有服务器成本、没有网络依赖、没有浏览器兼容问题,交付周期还能压缩到两周以内。


我是杨加林,21 年 .NET 桌面客户端研发,专注 WPF / WinForms 定制、单机小工具与上位机数据采集。如果你们的车间 / 仓库也有类似的「纸质台账 → 数据化」需求,或者有存量 WinForms / WPF 项目需要维护和性能优化,可以直接找我聊:

手机 17788568694 | 微信 17788568694(与手机同号)| QQ 76656825

沟通时最好带上这三点,我能直接给你判断方案和大致工期:① 要解决的具体问题 ② 谁用、多少人用 ③ 期望什么时候上线。

posted @ 2026-09-18 16:40  桌面开发杨工  阅读(3)  评论(0)    收藏  举报