C# + Scriban:生成 HTML 报表,自动发邮件(MES / 生产报表场景)

补充:这套方案在工厂圈子使用情况
  • MailKit 自动发邮件:大量 MES 在用
  • Playwright 自动截图 HTML 报表:不少工厂自动化报表在用,属于成熟方案,但不是每个项目都会上。很多项目嫌麻烦就直接 PDF 或者纯表格邮件。
  • Scriban + Playwright 组合:依然小众,因为大部分项目分开两套模板。你是利用 SSG 的模板复用,属于比较优雅的做法。

这个组合非常适合工厂场景,完整链路:

C# 读取 MES 数据库 → 计算指标(OEE、良率、产量)→ Scriban 渲染 HTML 报表模板 → 得到完整 HTML 字符串 分支两个用法:

  1. 保存成 .html 文件归档(存入文件服务器)
  2. 直接把 HTML 当做邮件正文,发送带样式图表的邮件报表

重点:不需要生成静态站点整套 dist 目录! 你只需要渲染单份 HTML 字符串,不需要输出文件也可以直接塞进邮件。当然也可以同时输出 HTML 文件作为附件。

整体包依赖

dotnet new console
dotnet add package Scriban
# 邮件:任选一套,常用MailKit(工业项目首选,稳定)
dotnet add package MailKit
dotnet add package MimeKit

1. 数据模型(生产日报示例)

public class DailyProductionReport
{
    public DateTime ReportDate { get; set; }
    public string Workshop { get; set; } = "一号车间";
    public double TargetQty { get; set; }     // 计划产量
    public double ActualQty { get; set; }    // 实际产量
    public double OEE { get; set; }          // 设备综合效率
    public double YieldRate { get; set; }    // 良率
    public List<ShiftData> ShiftList { get; set; } = new();
}

public class ShiftData
{
    public string ShiftName { get; set; } = "";
    public double Output { get; set; }
    public double OEE { get; set; }
}

2. Scriban 模板:ReportTemplate.scriban(HTML 报表,用于邮件正文)

⚠️ 邮件 HTML 有坑:大部分邮件客户端不支持外部 JS!ECharts 不能直接在邮件里渲染图表 两种方案:

  • 方案 A(推荐邮件):报表只用纯 HTML 表格 + 内嵌 CSS,图表预先生成图片,嵌入图片到邮件
  • 方案 B:HTML 报表作为邮件附件,用户打开附件 HTML,浏览器加载 ECharts 图表(下面演示这个,适合工厂内部邮件)
<!-- ReportTemplate.scriban -->
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<style>
table{border-collapse:collapse;width:100%;margin:10px 0;}
th,td{border:1px solid #ccc;padding:6px;text-align:center;}
th{background:#225;color:#fff;}
body{font-family:Microsoft YaHei;}
.info{margin:10px 0;color:#444;}
</style>
</head>
<body>
<div class="info">
    <h2>{{ ReportDate.ToString("yyyy-MM-dd") }} 生产日报【{{Workshop}}】</h2>
    <p>计划产量:{{TargetQty}},实际产量:{{ActualQty}}</p>
    <p>OEE:{{OEE}},良率:{{YieldRate}}</p>
</div>
<table>
    <tr><th>班次</th><th>产出</th><th>OEE</th></tr>
    {{ for item in ShiftList }}
    <tr>
        <td>{{item.ShiftName}}</td>
        <td>{{item.Output}}</td>
        <td>{{item.OEE}}</td>
    </tr>
    {{ end }}
</table>
<p>本报表为系统自动生成,仅供内部参考。</p>
</body>
</html>

3. 核心代码:Scriban 渲染 HTML + MailKit 发送邮件

using Scriban;
using MailKit.Net.Smtp;
using MimeKit;

namespace ReportMailDemo
{
    class Program
    {
        static async Task Main(string[] args)
        {
            // 1. 加载模板文本
            string templateText = await File.ReadAllTextAsync("ReportTemplate.scriban");
            var template = Template.Parse(templateText);

            // 2. 模拟从MES读取报表数据
            var reportData = new DailyProductionReport
            {
                ReportDate = DateTime.Now.Date,
                Workshop = "一号车间",
                TargetQty = 1000,
                ActualQty = 920,
                OEE = 0.86,
                YieldRate = 0.97,
                ShiftList = new List<ShiftData>()
                {
                    new ShiftData { ShiftName = "白班", Output = 500, OEE = 0.88 },
                    new ShiftData { ShiftName = "夜班", Output = 420, OEE = 0.83 }
                }
            };

            // 3. Scriban渲染得到完整HTML字符串
            string htmlContent = template.Render(reportData);

            // 4. 把HTML保存为归档文件(可选,存文件服务器)
            string savePath = $"report_{reportData.ReportDate:yyyyMMdd}.html";
            await File.WriteAllTextAsync(savePath, htmlContent);

            // 5. 构建邮件,HTML正文
            var message = new MimeMessage();
            message.From.Add(new MailboxAddress("MES系统", "mes@factory.com"));
            message.To.Add(new MailboxAddress("生产主管", "xxx@factory.com"));
            message.Subject = $"{reportData.ReportDate:yyyy-MM-dd} 一号车间生产日报";

            message.Body = new MultipartAlternative
            {
                new TextPart("plain") { Text = "请查看附件/HTML报表" },
                new TextPart("html") { Text = htmlContent },
                // 添加刚才生成的HTML作为附件(可选)
                new MimePart("text", "html")
                {
                    Content = new MimeContent(File.OpenRead(savePath)),
                    ContentDisposition = new ContentDisposition(ContentDisposition.Attachment),
                    ContentDisposition.FileName = Path.GetFileName(savePath),
                }
            };

            // 6. SMTP发送(企业邮箱/内部邮件服务器)
            using var client = new SmtpClient();
            client.Connect("smtp.xxx.com", 587, MailKit.Security.SecureSocketOptions.StartTls);
            client.Authenticate("mes@factory.com", "你的邮箱密码");
            client.Send(message);
            client.Disconnect(true);

            Console.WriteLine("✅ 报表HTML已生成,邮件发送完成");
        }
    }
}

🎯 两种图表方案(工厂报表最容易踩坑)

方案 1:邮件正文不带图表,HTML 附件带 ECharts(推荐简单)

上面代码就是这个思路:

  • 邮件正文只有文字表格;
  • 附件是 Scriban 生成的完整 HTML 文件,里面嵌入 ECharts;
  • 收件人下载附件,双击用浏览器打开,就能看到交互式趋势图。 ✅ 优点:模板写起来简单,Scriban 里直接写 ECharts 代码,和你做估值看板一模一样 ❌ 缺点:需要打开附件,邮件预览看不到图

方案 2:图片嵌入邮件正文(生产主管直接在邮箱预览看到图表,企业最常用)

流程:

  1. C# 计算完指标;
  2. Scriban 渲染 HTML(包含 ECharts 配置);
  3. 使用 Playwright 无头浏览器,加载 HTML,截图图表 PNG;
  4. 将图片作为内嵌资源嵌入 MailKit 邮件正文。

缺点:引入 Playwright,部署服务器需要安装浏览器依赖,稍微重一点。

✅ 这套方案在工厂系统的优势

  1. 模板分离:报表样式写在 Scriban 模板文件,业务 C# 代码不用改。UI 要调整表格样式、配色,只改 html 模板,不用重编译程序。运维人员改报表展示非常方便。
  2. 统一技术栈:MES 采集、数据清洗、指标计算、报表渲染,全部 C#。日志、异常捕获、重试机制复用你工业项目的代码。
  3. 可同时产出多份产物
    • 邮件正文 HTML
    • 归档 HTML 文件(文件服务器长期存档)
    • 可选:Playwright 转 PDF,一并作为附件
  4. 批量报表:循环多车间、多条产线,循环调用 Scriban 批量生成报表,批量群发对应负责人邮件。

⚠️ 坑点(工业项目重点)

  1. 邮件客户端限制:Outlook 对 HTML、CSS 支持很差,不要写复杂样式,尽量内联 CSS,不要外部 CSS 文件。
  2. ECharts 无法直接在邮件预览渲染:邮件客户端不执行 JS,这个是硬限制。记住:JS 图表只能放在 HTML 附件里,不能直接在邮件正文显示。
  3. 模板文件:生产部署,模板文件嵌入资源或者放到固定目录,做好缺失模板文件异常捕获。
  4. 定时调度:搭配 Hangfire / Windows 服务,每日凌晨自动跑,生成日报、自动推送邮件,非常适合 MES 日报。
  5. 编码:全部统一 UTF8,避免邮件中文乱码。

🧩 扩展:和你之前静态站点思路合并

如果你需要:

  1. 自动发邮件报表;
  2. 同时在内网 Nginx 生成整套静态报表站点(所有历史日报网页); 只需要在同一个控制台程序里:
  • Scriban 渲染单份报表 HTML 用于邮件;
  • 同时把报表 HTML 输出到 dist 目录,形成内网报表网站。一套模板两处复用。

对比传统方案

  • RDLC:导出 PDF,难做交互式图表;修改报表模板麻烦。
  • EPPlus:Excel 报表,适合数据,但没有可视化图表。
  • C#+Scriban:输出 HTML,可以交互式图表,方便邮件推送,模板修改简单。

如果你想要,我给你写带 ECharts 产量趋势图的 Scriban 模板(HTML 附件版本),直接嵌入到这个报表邮件代码里。

💥💥💥💥💥💥💥💥💥💥💥

「C# + Scriban 渲染 HTML,再用 MailKit 发邮件」这套组合,单独拆开每一块用的人非常多;但把 Scriban 作为 HTML 模板引擎来生成邮件正文的人不算多,属于小众但靠谱方案。

拆解来看

  1. MailKit + MimeKit:大量 .NET 工厂 / 企业项目在用(主流) MailKit 是 .NET 生态邮件库事实标准,几乎替代老旧的 SmtpClient。 MES、ERP、生产系统、告警系统,只要需要自动发邮件(日报、告警、质检报告),绝大多数 .NET 项目都会选 MailKit。这块是成熟、大量生产环境在用的。
  2. C# 动态生成 HTML 邮件:大量人在用,但模板方案不一样 大家都知道邮件适合 HTML 富文本。但是生成 HTML 字符串的常见做法:
  • 🔥 最多:字符串拼接 / StringBuilder(简单报表,表格少,快速写死 HTML)
  • 🔥 其次:RazorLight(Razor 模板),很多.NET 后端做邮件 HTML 模板首选,语法 @Model,C# 开发者熟悉
  • ⭕ 少量:Scriban、Handlebars.NET
  • 少数:Razor Pages 离线渲染

👉 大部分人做邮件 HTML,不会引入 Scriban。 原因:单纯邮件模板,很多人觉得 “没必要额外引入一个模板引擎”,简单报表直接拼字符串;稍微复杂一点就上 RazorLight。

Scriban 的主战场不是邮件,它的定位是静态站点生成器模板引擎。用它来做邮件 HTML 属于能力复用,不是它的主流场景。

那为什么工厂场景里很少看到 Scriban 做邮件报表?

  1. 邮件模板通常比较简单 邮件 HTML 大多是表格、文字,样式不能复杂(Outlook 限制)。很多生产日报就几行表格,开发觉得引入 Scriban 是多一个依赖,增加一点点复杂度。
  2. RazorLight 更贴合 .NET 开发者习惯 Razor 语法 @foreach @Model.Property,写惯ASP.NET的人零学习成本。Scriban 是 {{ }} 类 Jinja 语法,需要额外熟悉。
  3. 很多工厂系统直接走 PDF 附件路线 MES/OEE 日报,大量项目选择:生成 PDF 作为邮件附件,邮件正文只放简单文字。 RDLC、iTextSharp、QuestPDF,这些在工业报表圈子更普及,大家习惯 PDF 交付。

✅ 什么时候你依然适合选 Scriban?

当你本来就打算用 Scriban 做两件事,一套模板复用的时候,收益就很大:

  1. 生成 HTML 报表,保存到内网静态看板站点(dist 目录)
  2. 同一份模板,直接渲染 HTML 字符串,拿来当邮件附件

这才是这个方案的最佳切入点:一份模板,两处产出

  • 内网静态网站:打开 HTML,ECharts 交互式图表
  • 邮件:把同一个 HTML 作为附件发给管理人员

👉 这种场景下,Scriban 就不是 “为了发邮件额外引入的库”,而是你 SSG 系统已有的组件,零额外学习成本,一份模板维护一次。 这个是 RazorLight/StringBuilder 很难做到的:你要维护两套模板(网站一套,邮件一套)。

横向对比三个主流 .NET HTML 邮件方案

表格

方案使用人数优点缺点
StringBuilder 拼接 HTML最多无额外依赖,简单模板和代码耦合,复杂报表很难维护,改样式要改 C# 代码
RazorLightRazor 模板,很多企业在用Razor 语法熟悉独立模板;如果同时做静态看板,需要维护两套模板
Scriban 渲染 HTML少(小众)一套模板:同时用于内网静态看板 + 邮件附件多一个依赖;模板语法不是原生 C#;简单报表有点 “杀鸡用牛刀”

现实工程建议

  1. 如果你的需求只做邮件报表,不做静态看板网站:优先 RazorLight 或者 QuestPDF,社区案例更多。
  2. 如果你的需求同时要做内网静态报表看板 + 邮件推送报表:Scriban 就很合适,复用模板的收益 > 引入 Scriban 的成本。

刚好匹配你现在两条线:估值看板 SSG + MES 生产报表邮件。两个项目都可以复用这套 Scriban 模板思路。

补充坑点(不管选哪个模板引擎都要注意)

  1. 邮件正文 HTML,不要放外部 JS(ECharts 不能在邮件预览运行);只能放纯表格。
  2. ECharts 图表只能放在 HTML 附件里面,下载后浏览器打开。
  3. Outlook 兼容性差,CSS 尽量内联。
上一篇 C# + Scriban 做静态站点生成
下一篇 winform 开机启动
signoz和日志系统 elk的区别?

signoz和日志系统 elk的区别?

SigNoz vs ELK(Elastic Stack),结合你 C# 上位机 + PLC 采集 的场景 一句话概括: ELK:以日志全文检索为核心的日志栈(ES 倒排索引,擅长全文搜报错文本),指标、链路追踪是附加能力,多组件拼凑 SigNoz:OTel 原生一体化可观测平台,

2026-10-20
0 0 0
DateTime.UtcNow , DateTime.Now 有什么区别

DateTime.UtcNow , DateTime.Now 有什么区别

DateTime.UtcNow vs DateTime.Now(C#) 一句话总结 DateTime.UtcNow:获取世界协调时间(UTC,零时区),不受电脑系统时区影响。 DateTime.Now:获取本地时间 = UTC 时间 + 当前操作系统时区偏移。 详

2026-10-16
0 0 0
winform 开机启动

winform 开机启动

c# winform 写的采集程序。平时是登录 windows 桌面,手动点开运行 winform 程序。但是要改成开机启动,不要登录 windows 桌面也能执行 winform 程序。有没有什么办法?不要改造 winform 程序 先说核心结论 WinForm

2026-10-16
0 0 0
C# + Scriban:生成 HTML 报表,自动发邮件(MES / 生产报表场景)

C# + Scriban:生成 HTML 报表,自动发邮件(MES / 生产报表场景)

补充:这套方案在工厂圈子使用情况 MailKit 自动发邮件:大量 MES 在用 Playwright 自动截图 HTML 报表:不少工厂自动化报表在用,属于成熟方案,但不是每个项目都会上。很多项目嫌麻烦就直接 PDF 或者纯表格邮件。 Scriban + Playwright

2026-10-14
0 0 0
C# + Scriban 做静态站点生成

C# + Scriban 做静态站点生成

C# + Scriban 做静态站点生成(对标 baifenwei 的 generate_site.py) Scriban 是 .NET 生态下高性能模板引擎,语法类似 Liquid / Jinja2,刚好用来替代 Python Jinja2,写一个 C# 控制台程序(GenerateSite

2026-10-14
0 0 0
REA(rea.tools)“万物可逆向”:噱头很大,但不是真的什么都能逆向

REA(rea.tools)“万物可逆向”:噱头很大,但不是真的什么都能逆向

REA(rea.tools)“万物可逆向”:噱头很大,但不是真的什么都能逆向 全称 REA = Reverse Engineer Anything,“逆向一切” 只是项目口号,不是能力承诺。 https://rea.tools https://github.com/morluto

2026-10-14
0 0 0
采集上位机LOG文件,不停追加,文件大小50MB

采集上位机LOG文件,不停追加,文件大小50MB

txt 文件很大,40MB,50MB,txt 持续追加,这种场景。Logstash File Input、Flume Taildir、阿里云 Logtail、NewLifeX/X 日志查看工具 Klogg Log Parser Lizard https://githu

2026-10-10
0 0 0
AOI 图片上传 【分布式】方案

AOI 图片上传 【分布式】方案

车间 aoi 的相机图片上传,用飞牛 nas,群晖 nas,高并发,按 5TB / 天、60 天、500 台设备,能不能抗住?如果我在一台服务器放多个 ftp 服务,每条线一个 ftp 服务,端口 8001,第二条线 ftp 服务,端口 8002。ftp 靠谱还 nas 靠谱?稳定,生成环境

2026-10-09
0 0 0
MES 系统 的几种ID比较,雪花id,ULID,UUID

MES 系统 的几种ID比较,雪花id,ULID,UUID

实际场景:浏览器指纹和cookie的UUID 项目FingerprintJS visitorId bdc65ad12633d79792ceeca33a78e6c551.la 访客 ID 5a00c3da-ab80-513d-b7a7-894bcaaecf92来源读取浏览器硬件 / 软件特征,前

2026-10-09
0 0 0
ACCESS数据库访问报错

ACCESS数据库访问报错

AI最擅长找错,分析原因。 💥💥问题1:exclusively locked 因为ACCESS打开数据库后,表:TO_LOGGER_INFO ,当设计视图窗体时,另一个c#小程序去访问这个access数据库会报错: ERROR [HY000] [Microsoft][ODBC Mi

2026-09-29
0 0 0