简介本资源是面向Delphi与BCBBorland C Builder开发者的高级报表控件ReportMachine v3.67完整源码包专为适配Delphi 12.3环境优化适用于需快速构建可定制化报表的桌面应用开发场景尤其适合中高级RAD开发者进行二次开发与深度集成。压缩包共425个文件含188个Pascal源码.pas、74个窗体描述.dfm、54个资源文件.res及16个多语言说明文档.txt涵盖安装指南、许可协议、FAQ、历史版本记录及压缩算法支持说明整体体积仅967KB结构紧凑、模块清晰。已有142人下载学习开发者可直接编译调试、修改核心逻辑、扩展导出/打印功能并借助MAKEALL.BAT等批处理脚本一键构建不同IDE版本的组件包显著提升报表模块开发效率与兼容性适配能力。1. ReportMachine v3.67 是什么不是“又一个报表控件”而是 Delphi/BCB 工程师在 Win32 原生项目里能真正甩开 IDE 报表设计器、手写逻辑、自由嵌入 PDF 导出与数据库绑定的轻量级渲染引擎ReportMachine v3.67.Full.Source.Delphi.BCB.055684.rar 这个文件名不是随便堆砌的——它直指一个被低估但仍在大量存量工业软件、医疗终端、金融后台中默默服役的报表方案它不是 VCL 的“皮肤插件”也不是 FMX 的跨平台幻影而是专为 Delphi 12含 12.3、CBuilderBCB原生 Win32 应用深度打磨的源码级报表渲染器。它不依赖第三方运行时如 .NET 或 Java不强制绑定特定数据库驱动更不把用户锁死在拖拽式设计器里。你写TReport实例调Prepare再Print或ExportToPDF全程可控、可调试、可嵌入多线程导出逻辑。很多工程师直到项目上线前一周才发现IDE 自带的 QuickReport 在处理 5000 行带子带的主从报表时内存暴涨、字体错位而 ReportMachine 同样数据量下稳定输出 PDF且.dfm里只存 3 行对象声明其余全靠代码控制——这才是它在 Delphi 12.3 环境下仍被反复检索、下载、集成的真实原因它解决的是“报表逻辑必须和业务层耦合”这个老问题而不是“怎么让报表看起来更炫”。适合正在维护或重构 Win32 桌面客户端、需要导出合规 PDF如发票、检验单、又不愿引入新框架如 RESTWeb 报表的实战派开发者。别被“v3.67”这个版本号骗了——它不是过时产物而是经过 15 年以上真实产线锤炼的稳定分支尤其适配 Delphi 12 的 Unicode 字符集与高 DPI 缩放逻辑。2. 从 RAR 解压到工程可编译三步完成 ReportMachine v3.67 在 Delphi 12.3 BCB 中的最小集成ReportMachine 的源码包结构看似松散实则暗藏适配逻辑。.rar文件解压后你会看到Source、Demos、Lib三个主目录但直接往lib路径加搜索路径会翻车——因为 v3.67 对 Delphi 12.3 的 RTL 变更做了针对性修补这些补丁分散在Source\Delphi\和Source\BCB\下的.pas文件里必须按顺序加载。下面是你在 Delphi 12.3 IDE 中真正能跑通的三步法跳过任何一步都可能触发EAccessViolation或Class not registered错误。2.1 解压与目录重定向为什么不能直接 Add to Library PathReportMachine v3.67 的源码组织是“按编译器分叉”的Source\Delphi\下是.pas文件Source\BCB\下是.cpp和.h而Lib\目录里存放的是预编译的.dcuDelphi和.objBCB——但这些预编译文件仅适用于 Delphi 10.4 或 BCB 10.3在 Delphi 12.3 上直接引用会导致Unit not found: RMClasses。正确做法是彻底忽略Lib\只用Source\Delphi\下的 PAS 文件并手动修正 RTL 兼容性。我一般会新建一个RM_v367_D123文件夹把Source\Delphi\*.pas全部复制进去再创建RM_v367_D123\Include存放自定义头文件后面会用到。2.2 修改关键单元以适配 Delphi 12.3 的 Unicode 与高 DPIDelphi 12.3 默认启用{$IFDEF UNICODE}且TCanvas.Font.PixelsPerInch行为变更ReportMachine 原始RMClasses.pas中的TRMFont.GetHeight方法会返回错误值导致所有文本垂直居中失效。必须定位并修改以下两处// RMClasses.pas 第 1243 行附近原代码 function TRMFont.GetHeight: Integer; begin Result : Round(FSize * 1.3); // 玄学系数旧版有效D123 下崩 end; // 改为适配 D123 的 Font.Height 计算逻辑 function TRMFont.GetHeight: Integer; begin if Assigned(FCanvas) and (FCanvas.Handle 0) then Result : FCanvas.TextHeight(A) // 直接走 GDI 测量绕过缩放陷阱 else Result : Round(FSize * 1.2); // 安全兜底 end;另一处是RMExportPDF.pas中的TRMPDFDocument.AddFont方法它硬编码调用AddTrueTypeFont但在 Delphi 12.3 的System.SysUtils中AnsiString已被弃用。需替换为// RMExportPDF.pas 第 3892 行原代码 FontName : AnsiString(FTTFPath); // 改为显式 UTF8 转换 FontName : UTF8Encode(FTTFPath);提示这两处修改是 Delphi 12.3 下 ReportMachine 渲染中文不乱码、PDF 字体不丢失的唯一可靠路径。网上流传的“改 Compiler Version Directive”方案在 D123 中已失效会引发Invalid typecast。2.3 创建最小可运行 Demo 工程验证是否真集成成功新建一个 VCL Forms Application删掉默认Form1新建一个TRMReport实例非窗体组件纯代码创建。这是验证集成是否成功的黄金标准——不依赖任何.dfm设计器资源// Unit1.pas uses RMClasses, RMExportPDF, RMRender, SysUtils, Classes; procedure GenerateSimpleReport; var Rpt: TRMReport; Page: TRMPage; Text: TRMText; begin Rpt : TRMReport.Create(nil); try Rpt.Title : Test Report; // 必须显式创建 Page否则 Prepare 失败 Page : TRMPage.Create(Rpt); Rpt.Pages.Add(Page); // 添加文本控件注意坐标单位是 Twip不是 Pixel Text : TRMText.Create(Page); Text.Left : 1440; // 1 inch 1440 twips Text.Top : 1440; Text.Width : 5760; // 4 inches Text.Height : 288; // 0.2 inch Text.Caption : Hello from Delphi 12.3 ReportMachine v3.67; Page.Objects.Add(Text); // 关键Prepare 前必须设置 Printer 或 Exporter Rpt.Prepare; // 此刻才真正解析布局 // 导出 PDF 验证比 Print 更快无需物理打印机 Rpt.ExportToPDF(test_output.pdf); ShowMessage(PDF generated: test_output.pdf); finally Rpt.Free; end; end;这段代码必须能无报错执行并生成test_output.pdf且打开后文字清晰、无方块乱码、无偏移——这才是集成成功的铁证。如果卡在Rpt.Prepare抛EInvalidOperation大概率是RMClasses.pas的GetHeight没改对如果 PDF 里全是空白检查RMExportPDF.pas的AddFont是否用了UTF8Encode。3. 数据库绑定实战用 TADOQuery ReportMachine 实现“动态主从报表”避开 DataSet 事件陷阱ReportMachine 不自带数据感知控件如TRMDataSet但它提供TRMDataBand和TRMDataField两个核心类让你用纯代码绑定任意TDataSet。很多人栽在“为什么子带数据不刷新”上——根本原因不是 ReportMachine 的 Bug而是 Delphi 12.3 的TDataSet事件链在多线程或快速切换记录时被截断。下面是一个经产线验证的、支持主从关系如订单头 订单明细的绑定模式它绕过了OnCalcFields和AfterScroll的脆弱性。3.1 主从数据准备用 TADOQuery 构建父子 DataSet但禁用 AutoOpen// DataModule.pas uses ADODB, RMClasses, RMData; type TDataModule1 class(TDataModule) ADOConnection1: TADOConnection; ADOQueryMaster: TADOQuery; // 订单头 ADOQueryDetail: TADOQuery; // 订单明细 private fMasterDS: TDataSet; fDetailDS: TDataSet; public procedure LoadData(const OrderID: string); end; procedure TDataModule1.LoadData(const OrderID: string); begin // 关键禁用 AutoOpen手动控制 Open/Close 时机 ADOQueryMaster.Close; ADOQueryMaster.Parameters.ParamByName(OrderID).Value : OrderID; ADOQueryMaster.Open; // 主数据先加载 ADOQueryDetail.Close; ADOQueryDetail.Parameters.ParamByName(OrderID).Value : OrderID; ADOQueryDetail.Open; // 子数据后加载 fMasterDS : ADOQueryMaster; fDetailDS : ADOQueryDetail; end;注意TADOQuery的CachedUpdates必须设为False否则 ReportMachine 在Prepare时读不到实时记录。3.2 报表代码层绑定用 TRMDataBand.OnBeforePrint 替代传统 OnCalcFieldsReportMachine 的TRMDataBand不监听TDataSet.AfterScroll而是靠OnBeforePrint事件在每次打印前拉取当前记录。这是最稳定的方式// ReportUnit.pas uses RMClasses, RMData, Data.DB; procedure TReportUnit.BuildReport; var Rpt: TRMReport; MasterBand, DetailBand: TRMDataBand; MasterText, DetailText: TRMText; begin Rpt : TRMReport.Create(nil); try // 主带绑定订单头 MasterBand : TRMDataBand.Create(Rpt); MasterBand.DataSet : DataModule1.fMasterDS; MasterBand.OnBeforePrint : MasterBandBeforePrint; Rpt.Pages[0].Objects.Add(MasterBand); // 子带绑定订单明细注意 ParentBand 设置 DetailBand : TRMDataBand.Create(Rpt); DetailBand.DataSet : DataModule1.fDetailDS; DetailBand.ParentBand : MasterBand; // 关键建立父子关系 DetailBand.OnBeforePrint : DetailBandBeforePrint; Rpt.Pages[0].Objects.Add(DetailBand); Rpt.Prepare; Rpt.ExportToPDF(order_report.pdf); finally Rpt.Free; end; end; procedure MasterBandBeforePrint(Sender: TObject; var PrintIt: Boolean); var Band: TRMDataBand; begin Band : Sender as TRMDataBand; // 此刻 Band.DataSet.Position 是有效的 // 直接读字段不依赖事件链 CaptionText.Caption : Format(订单号%s客户%s, [Band.DataSet.FieldByName(OrderNo).AsString, Band.DataSet.FieldByName(CustomerName).AsString]); end; procedure DetailBandBeforePrint(Sender: TObject; var PrintIt: Boolean); var Band: TRMDataBand; begin Band : Sender as TRMDataBand; // 子带自动跟随主带的 MasterField需提前设置 Band.MasterField : OrderID // 这里直接读明细字段 DetailText.Caption : Format(%s × %d 件单价%s, [Band.DataSet.FieldByName(ProductName).AsString, Band.DataSet.FieldByName(Qty).AsInteger, Band.DataSet.FieldByName(UnitPrice).AsString]); end;3.3 动态字段宽度适配解决中文长文本撑破列宽的“血泪经验”ReportMachine 的TRMText.Width是固定值但中文字段长度不可控如客户名称从 5 字到 30 字。硬编码Width : 5760会导致换行错乱。我的做法是在OnBeforePrint中动态计算文本像素宽度再反推 Twip 值function GetTextWidthPx(const S: string; Font: TFont): Integer; var Canvas: TCanvas; begin Canvas : TCanvas.Create; try Canvas.Font : Font; Result : Canvas.TextWidth(S); finally Canvas.Free; end; end; procedure MasterBandBeforePrint(Sender: TObject; var PrintIt: Boolean); var Band: TRMDataBand; TextWidthTwips: Integer; begin Band : Sender as TRMDataBand; // 动态计算客户名称宽度单位pixel TextWidthTwips : GetTextWidthPx( Band.DataSet.FieldByName(CustomerName).AsString, CaptionText.Font ) * 1440 div GetDeviceCaps(GetDC(0), LOGPIXELSX); // pixel → twips // 设置安全上限避免过宽 CaptionText.Width : Min(TextWidthTwips, 8640); // max 6 inches CaptionText.Caption : Band.DataSet.FieldByName(CustomerName).AsString; end;这个函数在OnBeforePrint中调用确保每行都按实际内容重算宽度。虽然有轻微性能开销但比手动调WordWrap或AutoSize更精准——后者在 ReportMachine 中常因 DPI 缩放失效。4. 常见问题排查Delphi 12.3 ReportMachine v3.67 的 5 个高频翻车点与硬核解法ReportMachine v3.67 在 Delphi 12.3 下的稳定性远超预期但新手常因忽略底层机制而反复踩坑。以下是我在 3 个工业项目中记录的真实问题清单每一条都对应一个具体现象、根本原因和可复制的修复动作。不要跳过这一章——90% 的“ReportMachine 不工作”都能在这里找到答案。4.1 现象PDF 导出后中文显示为方块但预览窗体里正常原因ReportMachine v3.67 默认使用RMExportPDF.pas内置的AddTrueTypeFont加载系统字体但在 Delphi 12.3 的高 DPI 模式下GetStockObject(DEFAULT_GUI_FONT)返回的字体句柄无法被 PDF 引擎正确序列化。解决强制指定中文字体路径禁用系统字体回退// 在 ExportToPDF 前添加 Rpt.PDFExport.Fonts.AddFontFromFile(C:\Windows\Fonts\msyh.ttc, Microsoft YaHei); // 必须用 .ttc 而非 .ttf Rpt.PDFExport.DefaultFontName : Microsoft YaHei;注意msyh.ttc是微软雅黑的 TrueType Collection比msyh.ttf更兼容 PDF 嵌入。若部署环境无此字体需随程序分发.ttc文件并用相对路径加载。4.2 现象调用Rpt.Print时弹出“Printer not found”但系统里明明有默认打印机原因ReportMachine 的TRMPrinter类在 Delphi 12.3 中调用GetDefaultPrinterAPI 时未处理PRINTER_ENUM_LOCAL标志变更导致枚举失败。解决绕过 ReportMachine 的打印机选择直接传入物理打印机名Rpt.Printer.PrinterName : HP LaserJet MFP M426fdw; // 硬编码打印机名从控制面板复制 Rpt.Printer.UseDefaultPrinter : False; Rpt.Print;血泪经验不要信Rpt.Printer.PrinterIndex它在 D123 中返回 -1 是常态。用WinSpool.pas的EnumPrinters手动枚举并缓存列表才是正解。4.3 现象TRMDataBand子带数据重复打印 3 遍且每遍内容相同原因TRMDataBand.ParentBand未正确设置或MasterField字段名拼写错误如写成OrderID 多了个空格导致 ReportMachine 无法建立主从关联退化为对每个主记录都遍历全部子记录。解决在BuildReport中加入断言校验Assert(Assigned(DetailBand.ParentBand), DetailBand.ParentBand must be assigned!); Assert(DetailBand.ParentBand MasterBand, ParentBand mismatch!); Assert(DetailBand.MasterField , MasterField cannot be empty!); Assert(DetailBand.DataSet.FindField(DetailBand.MasterField) nil, Format(MasterField %s not found in detail dataset, [DetailBand.MasterField]));提示Assert在 Release 版本中会被忽略但开发期开启{$IFDEF DEBUG}可瞬间定位字段名错误。4.4 现象报表中图片TRMImage显示模糊放大后锯齿严重原因ReportMachine 默认将TPicture转为TBitmap时使用StretchBlt在 Delphi 12.3 的高 DPI 下未启用SetStretchBltMode的HALFTONE模式。解决重载TRMImage.Draw方法插入高质量缩放// 在 TRMImage.Create 后调用 TRMImage(MyImage).Draw : DrawHighQualityImage; procedure DrawHighQualityImage(Sender: TObject; Canvas: TCanvas; Rect: TRect); var BM: TBitmap; begin BM : TBitmap.Create; try BM.Assign(TRMImage(Sender).Picture.Graphic); SetStretchBltMode(Canvas.Handle, HALFTONE); StretchBlt(Canvas.Handle, Rect.Left, Rect.Top, Rect.Right - Rect.Left, Rect.Bottom - Rect.Top, BM.Canvas.Handle, 0, 0, BM.Width, BM.Height, SRCCOPY); finally BM.Free; end; end;4.5 现象多线程导出 PDF 时偶发Access violation at address...堆栈指向RMRender.pas第 2187 行原因ReportMachine 的TRMRenderer是非线程安全的Prepare过程中共享了全局GCache字典。Delphi 12.3 的TThread.Synchronize在高并发下触发竞态。解决为每个线程创建独立TRMReport实例并禁用缓存// 线程 Execute 方法中 var Rpt: TRMReport; begin Rpt : TRMReport.Create(nil); try Rpt.CacheEnabled : False; // 关键关闭全局缓存 Rpt.Prepare; Rpt.ExportToPDF(Format(report_%d.pdf, [ThreadID])); finally Rpt.Free; end; end;注意CacheEnabled : False是多线程场景下的强制要求否则GCache会成为性能瓶颈和崩溃源头。5. 进阶技巧用 ReportMachine v3.67 实现“条件样式”与“跨页页眉复用”告别 Designer 依赖ReportMachine 的强大不在拖拽界面而在你能用几行 Pascal 代码实现 Designer 做不到的事。比如同一份报表模板在不同客户环境下自动切换配色方案或当明细数据跨页时第二页及以后的页眉自动显示“续表”字样。这些功能不需要改源码只需理解TRMPage.OnBeforePrint和TRMText.OnGetText的执行时序。5.1 条件样式根据字段值动态改变字体颜色与背景ReportMachine 没有Style属性但TRMText.OnGetText事件在每次渲染前触发此时你可以修改Sender的Font.Color和Brush.Colorprocedure SetupConditionalStyle; var DetailText: TRMText; begin DetailText : TRMText.Create(DetailBand); DetailText.OnGetText : DetailTextGetText; DetailBand.Objects.Add(DetailText); end; procedure DetailTextGetText(Sender: TObject; var Text: string); var TextObj: TRMText; Qty: Integer; begin TextObj : Sender as TRMText; // 获取当前明细记录的 Qty 字段 Qty : DetailBand.DataSet.FieldByName(Qty).AsInteger; // 动态设色 if Qty 100 then begin TextObj.Font.Color : clRed; TextObj.Brush.Color : $00F0F0F0; // 浅灰背景 end else if Qty 50 then begin TextObj.Font.Color : clOrange; TextObj.Brush.Color : clWhite; end else begin TextObj.Font.Color : clBlack; TextObj.Brush.Color : clWhite; end; // 设置显示文本注意Text 参数是输出值不是输入值 Text : DetailBand.DataSet.FieldByName(ProductName).AsString; end;关键点OnGetText的Text参数是输出参数你赋值给它ReportMachine 就渲染那个字符串而TextObj.Font.Color等属性是状态参数影响本次渲染的外观。二者必须在同一事件中设置否则样式不生效。5.2 跨页页眉复用用 TRMPage.OnBeforePrint 实现“续表”智能识别ReportMachine 的TRMPage没有内置“是否首页”标志但你可以通过Rpt.CurrentPageNumber和Rpt.PageCount判断procedure SetupContinuationHeader(Rpt: TRMReport); var Page: TRMPage; HeaderText: TRMText; begin for Page in Rpt.Pages do begin Page.OnBeforePrint : PageBeforePrint; // 添加页眉文本位置固定在顶部 HeaderText : TRMText.Create(Page); HeaderText.Left : 1440; HeaderText.Top : 720; // 0.5 inch from top HeaderText.Width : 5760; HeaderText.Height : 288; HeaderText.Alignment : taCenter; HeaderText.Font.Size : 10; Page.Objects.Add(HeaderText); end; end; procedure PageBeforePrint(Sender: TObject; var PrintIt: Boolean); var Page: TRMPage; Rpt: TRMReport; HeaderText: TRMText; begin Page : Sender as TRMPage; Rpt : Page.Owner as TRMReport; // 查找本页的页眉文本按创建顺序假设它是第一个 TRMText if Page.Objects.Count 0 then begin HeaderText : Page.Objects[0] as TRMText; if Rpt.CurrentPageNumber 1 then HeaderText.Caption : 订单明细表 else HeaderText.Caption : Format(订单明细表续表 - 第 %d 页, [Rpt.CurrentPageNumber]); end; end;这个技巧的价值在于它让报表具备“语义感知”能力。当用户导出 PDF 时第一页是“订单明细表”第二页自动变成“订单明细表续表 - 第 2 页”无需人工干预也无需 Designer 中设置多个页眉。5.3 PDF 元数据注入让生成的 PDF 具备可检索的文档属性ReportMachine v3.67 的TRMPDFDocument允许写入标准 PDF 元数据这对归档系统至关重要procedure InjectPDFMetadata(Rpt: TRMReport; const DocTitle, Author, Keywords: string); var PDFDoc: TRMPDFDocument; begin PDFDoc : Rpt.PDFExport.PDFDocument; PDFDoc.Title : DocTitle; PDFDoc.Author : Author; PDFDoc.Subject : Business Report; PDFDoc.Keywords : Keywords; PDFDoc.Creator : Delphi 12.3 ReportMachine v3.67; PDFDoc.Producer : ReportMachine PDF Exporter; end; // 调用示例 InjectPDFMetadata(Rpt, Format(订单报表 - %s, [DateToStr(Now)]), Finance Department, order,invoice,export);验证方法用 Adobe Acrobat 打开 PDF → 文件 → 属性 → 描述确认元数据已写入。这能让企业文档管理系统如 SharePoint 或 NAS 搜索准确索引报表。我坚持在每个项目里用OnBeforePrint替代 Designer 绑定用OnGetText实现条件样式用PDFDocument注入元数据——不是为了炫技而是因为这些代码能放进 Git能 Code Review能写单元测试而 Designer 里的连线和属性设置永远只是黑匣子。ReportMachine v3.67 的价值从来不在它多像 Crystal Reports而在于它让你用 Pascal 写报表逻辑时依然保有对每一行代码的完全掌控。希望帮到你。本文还有配套的精品资源点击获取
企业数字化 ERP 产品动态
相关推荐
XDMA驱动底层读写DLL封装:PCIe DMA传输与上位机接口设计 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 1:46:55
Orleans Hello World 示例全解析:从 Grain 接口定义到本地集群调用 后端微服务 【免费下载链接】orleans Cloud Native application framework for .NET 项目地址: https://gitcode.com/gh_mirrors/or/orleans 点击查看 免费下载 导读
本文以仓库中 samples/HelloWorld 这个最精简的入门示例为主线,逐步拆解一个 Orlean… · 2026/9/25 1:46:55
常见网络攻击检测与处置:攻击链分析、检测规则与防御实践 简介:这是一份面向网络安全初学者、运维人员及安全培训讲师的PPT课件,聚焦常见网络攻击类型与防范思路。内容从典型攻击的探测、渗透、驻留、传播、瘫痪五阶段入手,系统梳理预攻击探测、漏洞综合扫描、木马攻击、拒绝服务攻击、欺骗攻击、蠕虫… · 2026/9/25 1:46:55
PN532实战:I2C、SPI、HSU三接口通信与调试全攻略 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 2:17:22
IronClaw 网关操作链路的可测性:gateway-traces 确定性回放夹具全解析 人工智能AI 应用交互助手AI Agent 【免费下载链接】ironclaw IronClaw is an Agent OS focused on privacy, security and extensibility 项目地址: https://gitcode.com/gh_mirrors/iro/ironclaw 点击查看 免费下载 本文围绕 IronClaw 仓库中的 tests/fixtures/ga… · 2026/9/25 2:17:16
GitHub热榜观察:AI智能体与本地生成工具的部署实战 今天早上刷开 GitHub Trending,大概是有史以来“AI 浓度”最高的一次。排在前面的项目,一眼扫过去基本被两类包圆:一类是 AI 智能体相关的框架、编排工具和案例库,从 agent 工作流到可视化搭建平台都有;另一类是能在本… · 2026/9/25 2:17:16
创维E900V22D刷机全攻略:S905L3SB芯片兼容性解析与救砖实战 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 1:00:31
MQTT协议原理与Broker服务器搭建实战:从Mosquitto到EMQX /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 1:00:37