简介Quick Report 4.05 是一套面向 Borland C Builder 6 与 Delphi 7 的报表生成控件包提供直观的可视化报表设计器、分组统计与多种导出格式并针对自定义纸张打印场景做了优化适合需要在应用程序中集成从简单列表到复杂业务单据的打印与报表预览功能的桌面开发者。资源包共65个文件以34个 Pascal 单元、15个窗体设计文件、7个资源文件为主体并附带编译工程文件压缩包仅247KB。内核源码完整开放涵盖报表引擎、数据源绑定、导出过滤及设计器相关模块便于开发者深入剖析报表生成机制或按项目需求二次定制同时随包提供的 .dcr、.dfm 等资源支持界面级调整可快速适配不同业务版式。已收录284人学习对于想精简报表依赖或研究经典报表组件实现原理的 BCB/Delphi 开发者是一份麻雀虽小五脏俱全的参考资源。1. 别被“Quick Report 4.05”劝退老 VCL 报表依然能打Quick Report 4.05 for BCB6 D7 这个组合第一眼确实像上世纪的古董。但只要你现在还在接触用 BCB6 或 Delphi 7 维护的老管理系统它反而是最顺手的打印方案设计期拖组件搭模板、运行期预览、直接打印也能导出 PDF不用另外部署服务。很多还在运行的生产系统单据、标签、统计表都是靠它出来的不是没人换是没必要换。这篇笔记就按我做老系统报表的套路把组件模型、安装顺序、最小可跑报表、必调参数和典型坑一次说清楚新手能照着落地老手也能对照边界。2. Quick Report 4.05 在 BCB6/D7 下的组件模型与工程集成2.1 为什么老系统还在用 QR而不是换一套选择报表方案先看的不是新不新而是改动量和风险。老系统里数据绑定已经是一套 ADO 或 BDE 逻辑换新报表等于把打印、预览、翻页、导出全部重写还牵涉到几十张已定稿的模板。Quick Report 4.05 在这里的优势是“所见即所得”设计期放一个 TQuickRep 在窗体上上面铺带区放上数据库字段立刻能看到字段在小计、分组里的真实位置。运行的时候数据源一赋值同一套组件直接出预览界面你不用自己画打印坐标。我用表格做一个粗略对比帮你在接手项目时快速判断“要不要留着它”。方案实现成本部署风险结论Quick Report 4.05模板已在 IDE 里改动点集中在带区和 DataSet低BPL 依赖固定适合老系统继续用导出 Excel 再打印需要写格式转换、合并单元格、页眉页脚中Excel 版本不一致会漂只适合给客户做数据导出不适合做正式单据自写 GDI 打印每个报表都要处理坐标、分页、缩放高维护成本指数上升除非只做固定尺寸小票否则不建议老系统里留下 Quick Report 还有一个原因它支持把报表模板和逻辑放在单独的 TQRQuickrep 窗体或数据模块里业务功能迭代时不影响打印。哪怕现在团队没人愿意碰老代码只要保持 QuickReport 版本不升级原来跑得好好的报表就不会因为第三方控件升级而“突然转圈”。这一点在后面的避坑章节还会提到。2.2 组件面板里到底有什么Quick Report 4.05 对 BCB6 和 D7 提供的是同一套 VCL 组件包安装后组件面板会多出一页 QuickReport。核心组件并不多先记这四个TQuickRep 是报表容器整个报表的纸张、页边距、数据集都挂在它上面TQRBand 是带区报表内容按带区划分比如标题带、页眉带、明细带、汇总带TQRDBText 用来输出数据集的某个字段TQRExpr 用来写表达式比如合计、计数、字符串拼接。还有一个容易忽略的 TQRPDFExportFilter它就是 4.05 导出 PDF 的入口。老版本里很多人导出 PDF 是打印到虚拟打印机其实这个版本已经支持直接调用导出过滤器只是默认没放在主面板上。需要时可手动加到窗体上或在代码里动态创建。后面第 3 章会给出具体用法。带区是 Quick Report 的骨架。TQRBand 里有个 BandType 属性rbTitle 表示整个报表只出现一次的标题带rbPageHeader 是每页页眉rbDetail 是数据明细rbSummary 是全部数据结束后的汇总。顺序不是靠你摆放的位置而是靠 BandType运行时 Quick Report 自动按固定顺序排版。这一点新手最容易反直觉在窗体上把明细带拖到标题带上面预览时依然是标题带在前。2.3 安装到 BCB6 和 D7包管理的正确顺序安装 Quick Report 4.05 到两个 IDE 最容易翻车的地方是“只装了设计期包”或“把 BCB6 和 D7 的包混用”。BCB6 与 D7 的二进制包后缀都是 BPL但内部编译器版本不同不能混着用。常见做法是源码目录里保留两套编译产物一套给 BCB6一套给 D7互不覆盖。先把运行时包编译出来再编译设计期包最后在 IDE 里 Install。# 假设源码解压在 D:\3rd\QuickReport405 # D7 使用 dcc32 命令BCB6 在 IDE 里打开同一份 dpk 后会自动走 C Builder 工具链 # 1) 清理老版本的 dcu 缓存避免与旧包冲突 del /q D:\3rd\QuickReport405\Packages\dcu\*.dcu # 2) 编译运行时包RT 包只提供组件实现不注册到 IDE 面板 cd D:\3rd\QuickReport405\Packages dcc32 -B -Q QR405_RT.dpk # 3) 编译设计期包DT 包负责把组件注册到 IDE 组件面板 dcc32 -B -Q QR405_DT.dpk # 4) 把编译产物复制到 IDE 的 Bpl 目录便于两个环境引用 copy Release\QR405_RT.bpl Release\QR405_DT.bpl $(DELPHI)\Projects\Bpl\参数里 -B 表示强制全量编译避免增量编译时拿了旧单元-Q 是静默模式只输出错误不然 IDE 控制台会被大量提示刷屏。dpk 文件名以你拿到的版本目录为准不同分发渠道命名略有差异常见叫 QR405_RT / QR405_DT也可能叫 qr4rt / qr4dt认准“运行时/设计期”这对概念即可。在 D7 里执行上面命令后打开 IDE 的 Component Install Packages手动 Add 一下 QR405_DT.bpl组件面板就会出现 QuickReport 页。BCB6 用户不用命令行直接通过 IDE 打开 dpk 工程Build 后 Install它自己会生成 BCB6 需要的 .bpi 和 .lib 导入文件。提示BCB6 里不要手动把 D7 编译出的 DCP 文件加进去。DCP 是 Delphi 专用BCB6 需要的是 BPI 和 LIB混用会报“Cannot find quickrpt.dcp”之类的错误。3. 从零搭出一张可打印报表数据绑定、预览与 PDF 导出3.1 手工建一张带标题和明细的表依赖包装好后先别急着在窗体上拖几十个组件。我的习惯是在运行时用代码搭最小报表确认数据链路通了再回去做模板布局。下面这段 BCB6 代码动态创建了一个 TQuickRep里面放一个标题带和一个明细带// QuickRep 是报表容器先指定数据集再往它身上挂带区 TQuickRep *rep new TQuickRep(Form1); rep-DataSet Form1-ADOQuery1; // 报表数据源ADOTable/ADOQuery 都可以 rep-Units QR_PIXELS; // 报表内部单位先设为像素便于观察 rep-Page-Width 794; // 竖向 A4 的像素宽约 105 毫米 rep-Page-Length 1123; // 像素高约 148 毫米 // 标题带整份报表顶部出现一次 TQRBand *titleBand new TQRBand(rep); titleBand-Parent rep; // Parent 必须是 QuickRep不是 Form titleBand-BandType rbTitle; titleBand-Height 600; // 600 像素约 1 厘米多按需调整 TQRLabel *lblTitle new TQRLabel(rep); lblTitle-Parent titleBand; lblTitle-Caption 客户订单明细; lblTitle-Left 150; lblTitle-Top 200; lblTitle-Font-Size 16; // 明细带每条记录循环一次 TQRBand *detailBand new TQRBand(rep); detailBand-Parent rep; detailBand-BandType rbDetail; detailBand-Height 400; TQRDBText *txtCustomer new TQRDBText(rep); txtCustomer-Parent detailBand; txtCustomer-DataSet rep-DataSet; // 绑定到 QuickRep 的数据集 txtCustomer-DataField 客户名称; txtCustomer-Left 20; txtCustomer-Top 50;代码本身不复杂但有三个关键点需要注意。第一所有 QuickReport 子组件的 Parent 必须设为带区或 QuickRep一旦设成 Form1运行到 Preview 时会报“Control has no parent window”或直接崩。第二BandType 决定渲染位置不是代码顺序你可以在 new 的时候先建明细带再建标题带预览时标题带依然在最上面。第三TQRDBText 的 DataSet 最好显式指定不要依赖 QuickRep 的继承同一个数据集套多个模板时会越改越乱显式指出来排查也方便。带区单位这里有个容易被带偏的设计TQuickRep 的 Units 有三种QR_PIXELS、QR_MM、QR_INCHES影响的是所有子组件的 Left、Top、Height 和 Page 尺寸。新手建议先全部用 QR_PIXELS等布局稳定后再切 QR_MM 做毫米精确定位。先切毫米会遇到“数字为什么小得看不见”的疑问其实是因为毫米单位下 600 像素变成了约 15 毫米视觉差异很大。3.2 把数据源接到带区DataSet 是核心Quick Report 的数据链路其实只有一条规则TQuickRep.DataSet 指到数据源TQRDBText.DataSet 也指到数据源DataField 填字段名。运行时数据源内部会维护当前记录指针QuickRep 每打印一条明细带就让它移动一次。这个机制很接近报表引擎的“游标循环”但代码里你不需要自己写 Next。我把常用带区列出来方便你对着模板理解BandType说明出现次数rbTitle报表标题整份 1 次rbPageHeader页眉每页 1 次rbDetail明细数据每条记录 1 次rbSummary汇总整份 1 次rbPageFooter页脚每页 1 次如果数据集根本没有记录QuickRep 也会生成一页只显示 Title 和 PageHeaderDetail 和 Summary 都不会输出。很多“为什么报表一张空页”的问题就是从这里来的数据集打开失败或条件查询返回空但 Title 带依然存在。第 5 章会专门讲这个现象。这里记住一个判断方法先在运行期给 QuickRep 的 BeforePrint 事件里写 ShowMessage(IntToStr(DataSet-RecordCount))看一眼取数是否为空比反复猜组件问题快得多。连接数据集之外还要关心字段类型。TQRDBText 对字符串、数字、日期都能直接显示但日期时间的默认格式由系统区域设置决定不同机器打出来可能是“2025/1/5”或“2025年1月5日”的差异。要固定格式用 TQRExpr 写表达式更可靠比如 FormatDateTime(yyyy-mm-dd, [DBText1])这样不依赖客户端区域设置跨机房打印也稳定。3.3 预览、直接打印和导出 PDF报表组好之后下一步是让用户能预览、打印和导出。Quick Report 自带预览窗口调用 Preview() 就能弹出来里面带缩放、翻页、打印按钮不需要额外开发。直接打印则调用 Print()但要注意它会直接走默认打印机弹打印任务没有二次确认生产环境要慎重。// 预览标准预览窗口带缩放和打印功能 rep-Preview(); // 直接打印走默认打印机不会再弹确认框 rep-Print(); // 导出 PDF使用 QuickReport 4.05 的导出过滤器 #include QRPDFExpt.hpp TQRPDFExportFilter *pdfFilter new TQRPDFExportFilter(D:\\reports\\order.pdf); try { pdfFilter-Resolution 300; // 导出精度300dpi 文字更清晰 rep-ExportToFilter(pdfFilter); // 触发整份报表导出 } __finally { delete pdfFilter; }参数说明里Resolution 控制 PDF 内部的渲染精度设 150 文件小但文字边缘会发虚设 300 打印出来基本接近真实打印效果。两个都用过的话我一般先出 150 看版式确认没问题再出 300 做正式件。要提醒一点TQRPDFExportFilter 构造函数的文件名路径必须存在否则导出时抛异常并中断而且异常信息不直观只提示“Invalid filename”。Preview() 和 Print() 都会触发 QuickRep 的内部准备流程先取数据再按带区排版分页。若 DataSet 是 ADOQuery在调用前必须保证 Active 为 true否则会出现“预览只有标题、没有明细”的情况。不是 QuickReport 不工作而是数据源没有打开。习惯上是把数据源打开和报表预览放在同一个方法里不要让报表依赖窗体其它地方的操作顺序。调用 Preview() 时还有个小坑预览窗口是模态的代码会停在 Preview() 这一行用户关掉窗口才返回。如果你在 Preview 后面紧跟着写“导出 PDF”的代码容易被误解为没有执行其实是预览窗口还没退出来。把导出功能放到某个按钮事件里不要放在 Preview 的同一段顺序代码中。4. 三个必调参数纸张方向、分组换页与行高4.1 纸张与边距的两种设置方式Quick Report 4.05 的纸张设置藏在 TQuickRep.Page 属性下面里面有 Width、Length、LeftMargin、RightMargin、TopMargin、BottomMargin还有一个 Orientation。这个 Orientation 是 Printers 单元里的枚举poPortrait 是纵向poLandscape 是横向。设计期内对应“报表设置”对话框运行期代码里可以这样控制// 横向 A4宽 297 毫米长 210 毫米 rep-Page-Orientation poLandscape; rep-Page-Width 297; rep-Page-Length 210; // 上面两个值按毫米写的前提是 Units QR_MM rep-Units QR_MM; // 页边距还是按毫米设置 rep-Page-LeftMargin 12; rep-Page-RightMargin 12; rep-Page-TopMargin 10; rep-Page-BottomMargin 10;这里有个很实际的边界条件Page-Width 和 Page-Length 的单位与 TQuickRep.Units 一致。如果你 Units 用 QR_PIXELS却把 Width 写成 297打印出来就是一张不到 4 厘米宽的小票而不是 A4。很多“之前好好的把单位改成毫米后报表变窄了”的问题都出在这个联动上。另一个要点是“设置纸张后打印服务器上的默认纸张会覆盖你的设置”。Quick Report 在 Windows 打印体系里走的是 DEVMODE当默认打印机是 A4 纸时程序里设置横向 A4 有效如果默认打印机是 80mm 热敏纸程序里设 A4 就会被驱动强制改成打印机的默认纸。这不是代码 bug而是打印驱动约束。真要在不同打印机上固定纸张最可靠的办法是在 QuickRep 的 PrinterSettings 里指定 PrinterIndex让报表只走某一台已经调好的物理打印机。4.2 分组报表怎么让每组从新页开始分组是 Quick Report 里最常用也最容易被点错的功能。分组的实现不是给 TQRDBText 加过滤条件而是用 TQRGroup 组件。TQRGroup 本质上是一个 band它和一个字段绑定字段值变化时QuickRep 知道“这一组结束了”。它有几个关键属性GroupField 或 Expression 用来指定分组依据ForceNewPage 属性设置为 true 时每组开始前强制分页FooterBand 指向一个 TQRBand用来做每组的小计。// 创建分组带按“客户编号”字段分组 TQRGroup *groupBand new TQRGroup(rep); groupBand-Parent rep; groupBand-BandType rbGroupHeader; // 分组页眉 groupBand-Expression 客户编号; // 4.05 里可以直接填字段名 // 每组第一行之前强制另起一页 groupBand-ForceNewPage true; // 分组页脚显示本组小计 TQRBand *groupFooter new TQRBand(rep); groupFooter-Parent rep; groupFooter-BandType rbGroupFooter; groupFooter-Height 300;注意到代码里 BandType 用 rbGroupHeader / rbGroupFooter这两个类型不能随意改。如果手动把某个普通 band 改为 rbGroupHeader但不把它和 TQRGroup 关联预览时会看到它只在第一页出现不会跟着分组变化。更隐蔽的是 TQRGroup 的 Expression 与 DataSet 字段名若不一致运行时不会报错但整个分组失效所有记录都挤在一个组里。判断依据很简单运行预览看明细里的字段值变化时带区是否同步输出分隔。没反应就看 Expression 是不是被写成了“字段名”而非字段文本比如多填了数据集别名。ForceNewPage 还有个连带效果每组第一页会重复打印 PageHeader。这个行为其实受欢迎很多单据要求每页都有表头。如果不想每页都出现表头把 PageHeader band 的 Enabled 设为 false只保留 Title 和 GroupHeader 即可。4.3 明细行高度宁可留白也别截断明细带高度直接影响每页能打多少行。TQRBand.Height 设大了页面上行数变少设小了TQRDBText 内容高过带区就被裁切。Quick Report 4.05 不会因为内容超高自动扩带它只按 Height 截断预览里看到文字突然少一半十有八九是这个原因。// 明细带高度建议不小于“字体高度 上下留白” detailBand-Height 400; // 若字体 10pt建议 380~450 // TQRDBText 自身也要有足够的 Height且开启按内容撑开 txtCustomer-Height 150; // 不与带区共用高度 txtCustomer-AutoSize false; // 关闭自动适应大小 txtCustomer-WordWrap true; // 允许换行避免文字横向截断TQRDBText 的 AutoSize 默认是 false因此组件高度固定。它和 Delphi 原生 Label 的 AutoSize 行为并不一样别套用“AutoSizetrue 就能自动扩展”的经验。在 Quick Report 里把 AutoSize 设为 true 反而会导致组件按当前数据内容重算宽度多记录场景下每行宽度不一样排版会来回跳。正确做法是固定 Height打开 WordWrap宽度留出 10%~15% 余量。带区高度则取“字段字体高度 8~15 像素”这样换行时还有一点余量不会立即截断。参数背后还有一个容易混淆的点Height 的单位跟随 TQuickRep.Units。在 QR_MM 下height400 是 400 毫米等于一整页放一条记录在 QR_PIXELS 下才是合理的行高。因此遇到“一行明细占一页”的怪事先查 Units再查 Height。这两个值的联动是 Quick Report 4.05 最容易出现“玄学”的地方实际上都是单位换算没有统一。5. 踩坑记录QR4.05 在 BCB6/D7 下的乱码、空白页与包冲突5.1 BPL 冲突D7 装完BCB6 工程报找不到包现象Delphi 7 里 Quick Report 4.05 用得好好的换到 BCB6 打开同一套工程提示“Cannot find QuickRpt 包”或 IDE 启动时弹找不到 BPL。原因BCB6 与 D7 使用相同后缀的 BPL但二进制格式由各自编译器生成内部带版本标识互不识别。很多人把 D7 编译出来的 BPL 直接用 BCB6 加载结果 BCB6 认为包不存在。更严重的情况是把两个版本 BPL 放在同一目录且同名后装的覆盖了先装的两边 IDE 同时崩。解决把两个 IDE 的包目录完全分开D7 的产物放 $(DELPHI7)\Projects\BplBCB6 的放 $(BCB6)\Projects\Bpl并在源码目录里保留两套独立 DCU。再用第 2 章的步骤分别编译安装。排查时先看 IDE 报错的是哪个 BPL 路径再确认该路径下文件的修改时间是否被另一套覆盖。5.2 预览正常打印出来的中文是“方块/乱码”现象预览窗口里中文显示正常打印出来中文变成框或问号。原因Quick Report 4.05 预览时用屏幕字体渲染打印时按 TQuickRep.Font 和各个组件的 Font 选择打印机字体。老模板里 Font.Name 默认是 “Times New Roman”它没有中文字形系统自动字体替换失败后打出乱码。解决全局设置报表字体为宋体或微软雅黑。在 QuickRep 的 OnBeforePrint 事件里统一修正而不是逐组件改。代码加一句 rep-Font-Name 宋体并把这个 Font 沿用到所有子组件子组件没显式设置 Font 时会继承。若只改标题不改明细打印时明细仍会乱。建议在创建带区时统一在循环里对每个子组件执行 同上赋值。验证方式是把导出的 PDF 与预览对照PDF 中不乱码一般打印机也不乱。5.3 导出 PDF 中文变方框别在滤镜里找字体现象ExportToFilter 出来的 PDF中文全是方框但报表本身预览正常。原因Quick Report 4.05 的 PDF 导出滤镜不嵌入中文字体它对字体名做映射遇到宋体这类中文字体名时数据不完整最终以 PDF 默认字体渲染。这个行为在滤镜参数里看不出端倪因为报错信息很少。解决两个常见做法。一个是把报表内所有中文相关组件字体改成系统已映射的 “SimSun”导出时部分版本能正确映射但不同打印机、不同系统语言环境下行为不一致。另一个是避开原生 PDF 导出用系统自带的虚拟 PDF 打印机打印一次再拿到 PDF 文件。老项目里稳定优先我一般用虚拟打印机方案对最终用户来说结果一样还能沿用已有的打印机配置。如果你坚持用 TQRPDFExportFilter至少先做字体映射测试确认目标机器上导出的 PDF 文字可选中、可复制再推广到生产。5.4 报表末尾多出一张空白页现象数据只有两页打印或预览时末尾多出一页白纸。原因最常见的是 Summary 带要么为空但高度不为零要么某个 band 的 ForceNewPage 在最后一条记录后又被触发了一次。第二种多见于 GroupFooter 高度非零且紧跟一个空 Detail 带导致 QuickRep 判断还有内容需要另起一页。解决把末尾所有非必要带的高度设为 0 或直接 Enabledfalse尤其是名为“备注”“空行”的装饰 band。然后在 QuickRep 的 OnEndPage 里判断当前记录是否为最后一条若 DataSet-Eof 且当前页码大于总页数则取消输出。判断逻辑也可以放在 AfterPrint 里直接数导出的 PDF 页数比在预览里目测更准确。5.5 设计期一切正常运行期 Preview() 报打印机相关异常现象同一台机器设计器里预览正常编译运行后 Preview() 抛“选定的打印机无效”或 EOleException。原因Quick Report 预览时会先向系统默认打印机查询纸张和打印分辨率。目标机器上默认打印机是网络打印机且网络断开或驱动异常查询返回失败预览就起不来。开发机上用的是本地虚拟打印机所以开发时永远复现不了。解决在代码里指定预览可用的打印机。最简单的是在程序启动时检查 Printer()-Printers 列表把第一台可用的物理打印机设为默认。报表侧设置 rep-PrinterSettings-PrinterIndex 为这台打印机的索引确保预览和打印都走同一台。还有一种兜底方案是给预览按钮做一个异常处理捕获到打印机异常后临时切换默认打印机再调用 Preview()避免用户点一下报表程序就退出。这个坑在部署给客户时最容易遇到建议作为上线检查清单里的固定检查项。6. 进阶用法动态字段列模板与回归验证6.1 按 DataSet 字段数量动态生成明细列很多客户报表字段不固定设计期摆好 10 列数据源加了第 11 列就要改模板。Quick Report 4.05 支持运行期动态创建 TQRDBText按 DataSet 的 FieldCount 循环生成列宽度按字段名长度估算这样可以少维护很多模板。代码里要控制每个新组件的 Parent 指向明细带并手动设置 Left否则所有列会叠在同一个位置。// 动态生成明细列字段有多少列就生成多少 TQRDBText int colLeft 10; for (int i 0; i rep-DataSet-FieldCount; i) { TField *f rep-DataSet-Fields-Fields[i]; TQRDBText *cell new TQRDBText(rep); cell-Parent detailBand; // Parent 必须是明细带 cell-DataSet rep-DataSet; cell-DataField f-FieldName; cell-Left colLeft; cell-Top 10; cell-Width 120; // 固定宽度超过 120 截断 colLeft 130; // 列间距留 10 像素 }这里要注意两点动态组件也要在 Report 结束时释放否则第二次预览时旧组件还在带区里新组件又加一层报表越打越密。建议在 QuickRep 的 OnBeforePrint 里先遍历 Components把类型为 TQRDBText 的旧组件清掉再重新生成。另一个是字段宽度中文客户名、备注字段建议单独按字段名或字段 Size 判断宽度不要所有列一刀切。只要这一版固定了模板宽度后续换数据源也只是改循环里的宽度参数。6.2 用 PDF 存档做回归比对改完报表别急着发给客户。我的习惯是先跑一个固定数据集导出一份 PDF 存档标上“基线版本”。之后每次动带区、改字体、调整分组再导一份新 PDF 对比页数和关键内容。对比时不用肉眼逐行用文本抽取工具把两份 PDF 的文字各抽一遍重点比对页数、合计金额、客户名称三样。数量、金额这类字段一旦变更立即能看出来版式问题则靠页数差异捕捉。这个方法对付“打印几行才分页”“合计放到下一页”这类改动非常有效比反复点预览快得多。老项目改动报表最怕的是开发机正常、客户机打出来分页差异。PDF 存底至少证明改动前后同一数据源下的输出是一致的能堵住很多说不清的责任边界。我给自己定了一条规矩任何一次改完报表先导 PDF再决定要不要卸载旧包。这样做的好处是改崩了也有后悔药拿基线 PDF 一对比就知道差异在哪个带区。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?