资讯动态

WinCC SQL报表:VBScript写库与MSFlexGrid查询导出

发布时间:2026/10/30 2:53:03 来源:尧图企业网站定制
简介面向WinCC组态与工业自动化数据报表开发场景这份技术文档讲解如何借助VBScript、SQL数据库及ActiveX控件实现生产数据查询与日报表输出适合已具备WinCC基础、需要补足SQL交互与报表组态能力的工程师参考。压缩包内为1个doc文件大小约1.11MB内容涵盖SQL Server 2005建库建表、WINCC全局脚本写入变量、日报表画面组态、MSFlexGrid与MS DTPicker控件配置以及“查询”“打印”按钮的脚本编写。文档还说明全局脚本触发条件、ActiveX控件添加与名称修改、按起止日期拼接SQL查询条件以及将表格数据转存Excel后打印的实现方式附录给出写入与查询数据库的VBS代码示例并提示控件注册、日期范围校验等排错点便于读者搭建从数据采集、存储到表格展示和打印输出的完整链路。已有1682人学习可作为工控报表项目的实操参考。1. 交接班报表还在手抄这套 VBS 控件的 WINCC SQL 报表能省掉什么凌晨两点设备报警第二天交班要的是前一天 0 点到 24 点的 CPU、内存、硬盘占用表——WinCC 自带的趋势控件能画曲线但领导要的是一张能按日期筛选、能打印、能签字的表格。这套基于 VBScript 的 WINCC 报表方案核心就三件事全局脚本把内部变量周期写进 SQL Server 的报表表MSFlexGrid 控件把查询结果铺成网格MS DTPicker 控件选起止日期。它不引入任何报表服务器也不装第三方插件WinCC 装完、SQL Server 装完剩下的就是写脚本和摆控件。适合手上跑着 WinCC 项目、需要日报/班报、又不想为几张表单独上一套报表系统的人同样适合接手老项目、发现前任只留了一堆 .doc 说明的维护者。2. 建库与写库从 WinCC 变量到 SQL 表的四步链路2.1 先把表结构定死字段、类型与时间列资源里的建库步骤是纯图形操作打开 SQL Server Management Studio连上实例在 Databases 上右键 New Database输入库名再到 Tables 上右键 New Table逐列填 Column Name 和 Data Type。这套点法在 2005 那个年代没问题但换台机器复现时容易漏字段、漏索引我更习惯先用 T-SQL 把结构固化成脚本再在图形界面里核对。-- 建库库名与后续脚本里的 Initial Catalog 必须一字不差 CREATE DATABASE [SQL-Report]; GO USE [SQL-Report]; GO -- 报表主表一行代表一个采集时刻的三个指标 CREATE TABLE dbo.VBSReport ( [time] DATETIME NOT NULL, -- 采集时刻查询和排序都靠它 cpu FLOAT NULL, -- CPU 占用百分比 ram FLOAT NULL, -- 内存占用百分比 hd FLOAT NULL -- 硬盘占用百分比 ); GO -- 按时间查是唯一的高频访问路径索引直接建在这一列 CREATE CLUSTERED INDEX IX_VBSReport_time ON dbo.VBSReport([time]); GO字段类型选 FLOAT 而不是 INT因为HMIRuntime.Tags(cpu).Read读回来的值本身可能是带小数的浮点落库时被截成整型后面算平均值就会跟画面显示对不上。时间列用 DATETIME 而不是字符串是为了让BETWEEN能走索引如果存成varchar2024-1-5和2024-01-05会被当成两个不同的值排序月底对账时能让你怀疑人生。提示库名、表名、列名这三个东西后面每一段脚本里都会出现。资源附带的写入脚本用的是SQL-Report库、VBSReport表而查询脚本里写的是Example-report库、report表这两组名字对不上——直接抄下来跑结果就是「写进去了但查不出来」。名字统一这件事建表时就要定别等到联调。2.2 全局脚本里的 action把三个变量写进一行记录写库动作放在 WinCC 的全局脚本编辑器里不是画面脚本。这一点新手经常混画面里按钮的OnClick能用ScreenItems(控件名)拿到控件对象全局脚本里没有画面上下文只能通过HMIRuntime.Tags(变量名).Read读变量。全局脚本的返回值、报错都进不了画面所以错误处理必须自己写。 WinCC 全局脚本 - 动作触发器设为 1 分钟定时 Option Explicit Function action Dim cn, cmd, strcn Dim a, b, c, d On Error Resume Next 脚本宿主不弹窗先兜住错误 a Now 采集时刻 b HMIRuntime.Tags(cpu).Read c HMIRuntime.Tags(ram).Read d HMIRuntime.Tags(hd).Read Data Source.\wincc 指本机的 wincc 命名实例 Initial Catalog 就是库名SSPI 表示用当前 Windows 账户身份登录 strcn ProviderSQLOLEDB.1;Integrated SecuritySSPI; _ Persist Security InfoFalse;Initial CatalogSQL-Report; _ Data Source.\wincc Set cn CreateObject(ADODB.Connection) cn.ConnectionString strcn cn.Open If Err.Number 0 Then HMIRuntime.Trace 写库连接失败: Err.Description vbCrLf Exit Function End If 用 Command 参数避免时间字符串里的单引号把语句拼断 Set cmd CreateObject(ADODB.Command) Set cmd.ActiveConnection cn cmd.CommandType 1 adCmdText cmd.CommandText INSERT INTO VBSReport([time],cpu,ram,hd) VALUES(?,?,?,?) cmd.Parameters.Append cmd.CreateParameter(t, 135, 1, , a) 135 adDBTimeStamp cmd.Parameters.Append cmd.CreateParameter(c, 5, 1, , b) 5 adDouble cmd.Parameters.Append cmd.CreateParameter(r, 5, 1, , c) cmd.Parameters.Append cmd.CreateParameter(h, 5, 1, , d) cmd.Execute If Err.Number 0 Then HMIRuntime.Trace 写库失败: Err.Description vbCrLf End If cn.Close Set cmd Nothing Set cn Nothing End Function连接串里四个部分各有分工。ProviderSQLOLEDB.1是老的 OLE DB 提供程序WinCC 这种 32 位宿主环境用它最稳Integrated SecuritySSPI表示走 Windows 集成认证意味着 WinCC 运行时那个账户必须在 SQL Server 上有写权限域环境换账户后脚本突然写不进去八成就是这个原因Data Source.\wincc里的点代表本机wincc是安装时创建的命名实例不是库名别把两者搞混。资源原稿用的是字符串拼接insert into ... Values(a,b...)。内部变量是数值拼接一般不出事但只要有一个变量是字符串、里面带了单引号整条语句立刻语法错误。参数化写法多几行但省掉的是半夜查日志的时间。CreateParameter的第三个参数 1 表示adParamInput第四个参数留空让 ADO 自己推长度数值类型不填也能跑。2.3 触发条件周期触发和变量触发选哪个原稿在数据库那一步特意标了一句「注意全局脚本触发条件」这句话是整个方案里最容易被忽略的部分。WinCC 全局脚本的触发器分两类定时器和变量。定时器按固定周期执行比如 60 秒一次变量触发在指定变量值变化时执行。选定时器的好处是数据密度均匀做日报表时每个小时的样本数一致画出来的平均值有可比性坏处是不管变量变没变都插一行一天 1440 条一个月四万多条两年后这张表能拖慢整个查询。选变量触发的好处是没有冗余记录坏处是变量稳定时完全没有数据某台设备一下午没波动报表上就是一片空白看起来像采集中断。我一般用定时器加条件写入的组合定时器 60 秒跑一次脚本里先跟上一条记录比较三个值都没变化就跳过。 在 action 开头加一段变化检测 Dim lastCpu lastCpu HMIRuntime.Tags(lastCpu).Read 内部变量记录上次写入值 If Abs(b - lastCpu) 0.1 Then Exit Function 变化小于 0.1 视为没变 End If HMIRuntime.Tags(lastCpu).Write b这个 0.1 的阈值不是拍脑袋模拟量采集本身带抖动阈值太小等于没过滤阈值太大又会把真实的缓慢爬升吃掉。用之前先看两周的原始数据估算一下稳态时的波动幅度取它的两到三倍比较靠谱。另外lastCpu这类辅助变量要设成掉电保持否则 WinCC 一重启就丢重启后第一条记录必然重复写入。写入链路跑通之后别急着做画面。先打开 Management Studio对着表执行一次SELECT TOP 50 * FROM VBSReport ORDER BY [time] DESC确认时间在走、三个值不是全 0、记录间隔符合预期。这三条任何一条不满足后面画面做得再漂亮也是白搭。3. 控件注册与画面组态MSFlexGrid 和 DTPicker 怎么摆才不翻车3.1 OCX 注册为什么必须单独做这一步MSFlexGrid 和 MS DTPicker 都是 ActiveX 控件不是 WinCC 自带的对象。资源里的做法是在画面的 ActiveX Controls 上右键 Add/Remove从列表里勾选Microsoft FlexGrid Control, Version 6.0和Microsoft Date and Time Picker Control 6.0 (SP4)。能在这两个名字前面打勾前提是系统里已经注册过对应的 OCX 文件没注册过列表里根本看不到。注册命令本身不复杂麻烦的是路径。很多机器装过 VB6安装程序顺手把MSFLXGRD.OCX注册好了所以「直接就能选」换一台干净的工控机就得手动拷文件、手动注册。# 64 位系统上32 位 OCX 应该放在 SysWOW64 而不是 System32 copy MSFLXGRD.OCX C:\Windows\SysWOW64\ # 用 SysWOW64 目录下的 regsvr32 注册否则会提示模块加载失败 C:\Windows\SysWOW64\regsvr32.exe C:\Windows\SysWOW64\MSFLXGRD.OCX # 卸载时加 /u C:\Windows\SysWOW64\regsvr32.exe /u C:\Windows\SysWOW64\MSFLXGRD.OCX这里有个流传很广的错误做法把 OCX 拷进C:\WINDOWS\system32再注册。在 32 位系统上没问题在 64 位系统上就翻车——system32目录放的是 64 位组件而 WinCC 是 32 位进程它去加载 64 位的 OCX 会直接报「类未注册」。判断方法很简单任务管理器里看 WinCC 运行进程有没有*32后缀有就是 32 位控件一律往SysWOW64放注册也用SysWOW64下的 regsvr32。注意注册需要管理员权限。普通账户执行 regsvr32 会返回「DllRegisterServer 调用失败」看错误码容易误判成文件损坏。用管理员身份开命令行再执行一遍。3.2 DTPicker 的取值日期控件返回的到底是什么DTPicker 在画面上表现为一个下拉日历属性里能设格式。脚本中取值的写法是ScreenItems(Date1).Value返回的是一个日期型值直接喂给Year()、Month()、Day()就能拆出年月日。 画面脚本里读两个日期控件 Dim Date1, Date2, By, Bm, Bd, Ny, Nm, Nd Set Date1 ScreenItems(Date1) Set Date2 ScreenItems(Date2) By Year(Date1.Value) : Bm Month(Date1.Value) : Bd Day(Date1.Value) Ny Year(Date2.Value) : Nm Month(Date2.Value) : Nd Day(Date2.Value) 拼成 SQL 认识的字符串月日补零避免区域设置差异 Dim BeginDate, EndDate BeginDate By - Right(0 Bm, 2) - Right(0 Bd, 2) 00:00:00 EndDate Ny - Right(0 Nm, 2) - Right(0 Nd, 2) 23:59:59补零这一步不能省。原稿是By - Bm - Bd月份是 1 位时得到2024-1-5 00:00:00。这个字符串在部分语言环境下 SQL Server 能认在另一些环境下会被当成yyyy-dd-MM解析1 月 5 日直接变成 5 月 1 日查出来的数据完全是另一个区间而且不报错。这种错误最恶心的地方在于它是静默的报表能出、数字有就是不对。DTPicker 还有一个属性值得看一眼Format。设成dtpCustom之后配合CustomFormat可以控制显示但脚本取值不受显示格式影响取到的始终是完整日期。有人为了「只显示年月」把CustomFormat设成yyyy-MM结果以为取到的值也只剩年月这是误会。3.3 MSFlexGrid 的属性行列、合并与对齐MSFlexGrid 本质是一张能设属性的二维表脚本里通过TextMatrix(行, 列)读写单元格行列都从 0 开始。资源里用到的关键属性如下这张表建议直接抄进笔记属性典型取值作用Rows记录数 6总行数多出来的 6 行留给表头、MAX/MIN/AVGCols5列数序号、时间、CPU、内存、硬盘FixedRows2前两行固定不随滚动条移动ColWidth(n)900 / 1900 / 1000 …单列宽度单位是 twip时间列要宽一些MergeCells4按行列双向限制合并配合 MergeRow 用MergeRow(0)True第 0 行整行合并成一个大标题ColAlignment(n)4垂直水平都居中数值列看起来才整齐Rows必须在填数据之前设好。先填后设会把已填内容清空这个顺序错了表现是「查询有结果但表格空白」。MergeCells 4对应的是「行列都受限的合并」只有相邻单元格内容完全一致时才会合并所以第 0 行要先给五列都赋同一个字符串合并才会生效。控件拖到画面上之后建议在属性面板里把Name改成有意义的名称比如gridReport、dtpBegin别用默认的MSFlexGrid1。脚本里靠名字找对象名字一改脚本里所有ScreenItems(...)都要同步改反过来先定名字再写脚本返工量小得多。4. 查询脚本拆解一条 SQL 怎么变成明细表加 MAX/MIN/AVG 统计行4.1 时间区间为什么用 23:59:59 而不是「小于次日零点」查询条件写成BETWEEN 起始 00:00:00 AND 结束 23:59:59这是最常见的写法也是最容易漏掉最后几百毫秒的写法。DATETIME 的精度是 3.33 毫秒23:59:59.997这样的值不在区间内如果采集周期正好落在这一带当天的最后一条就丢了。严格的写法是左闭右开-- 推荐左闭右开不依赖秒级精度 WHERE [time] 2024-01-05 00:00:00 AND [time] 2024-01-06 00:00:00但在 WinCC 画面脚本里用户选的是两个日期右边界要现算「次日」。VBScript 里DateAdd(d, 1, Date2.Value)就能得到拼字符串时再接00:00:00。资源原稿用的是23:59:59在采集周期为分钟级的场景下够用因为分钟级数据不会落在 59.997 秒这种位置但如果哪天把采集周期改成秒级或者事件触发这个边界就必须换掉。还有一层校验起始日期不能晚于结束日期。原稿用了一长串If ByNy Or ByNy And BmNm Or ...来判断逻辑是对的但只弹了个提示框没有Exit Sub用户点完确定脚本继续往下跑照样去查一个空区间。加上退出才是完整逻辑If By Ny Or (By Ny And Bm Nm) Or (By Ny And Bm Nm And Bd Nd) Then MsgBox 起始时间不能晚于结束时间, vbOKOnly, 时间范围错误 Exit Sub End If4.2 连接与游标RecordCount 为什么总是 -1ADO 的 Recordset 默认用服务端游标这种游标是「只进」的RecordCount返回 -1因为服务端根本没打算知道总行数。要拿到准确条数必须把连接对象的CursorLocation设成 3adUseClient让结果集整个拉到客户端再统计。Dim conn, oRs, oCom, Sql Set conn CreateObject(ADODB.Connection) conn.ConnectionString ProviderSQLOLEDB.1;Integrated SecuritySSPI; _ Persist Security InfoFalse;Initial CatalogSQL-Report; _ Data Source.\wincc conn.CursorLocation 3 adUseClient关键的一行 conn.Open Set oCom CreateObject(ADODB.Command) oCom.CommandType 1 Set oCom.ActiveConnection conn oCom.CommandText Sql Set oRs oCom.Execute If oRs.RecordCount -1 Then MsgBox 结果集不支持计数请检查 CursorLocation, vbOKOnly, 游标错误 Exit Sub End If客户端游标有代价十万行以上会全量拉进内存画面脚本卡住。日报表一天最多几千行无所谓如果哪天要做月度报表就该改成用SELECT COUNT(*)单独查一次总数明细照样走服务端游标分页取。明细 SQL 和统计 SQL 是两条独立的语句原稿用两个 Command 对象分别执行这个思路是对的——一条查明细用于铺表格一条用AVG/MIN/MAX聚合出统计行不要试图在一条语句里同时拿到明细和聚合那要靠窗口函数老版本 SQL Server 还不支持。-- 明细把时间格式化成 19 位字符串避免脚本里处理日期 SELECT CONVERT(char(19), [time], 20) AS shijian, cpu, ram, hd FROM VBSReport WHERE [time] BETWEEN 2024-01-05 00:00:00 AND 2024-01-05 23:59:59 ORDER BY [time]; -- 统计一次算出九个值供表格底部三行使用 SELECT AVG(cpu) AS ylp, AVG(ram) AS wdp, AVG(hd) AS llp, MIN(cpu) AS ylx, MIN(ram) AS wdx, MIN(hd) AS llx, MAX(cpu) AS yld, MAX(ram) AS wdd, MAX(hd) AS lld FROM VBSReport WHERE [time] BETWEEN 2024-01-05 00:00:00 AND 2024-01-05 23:59:59;CONVERT(char(19), [time], 20)里的 20 是 ODBC 标准格式yyyy-mm-dd hh:mi:ss格式化在数据库侧做完脚本里就不用管区域设置。这个转换会让时间列上的索引失效在 WHERE 里但这里只用在 SELECT 列表不影响查询计划。4.3 填表TextMatrix 的行列偏移与三个统计行填表逻辑的核心是行列对应关系。第 0 行是合并标题第 1 行是列名第 2 行开始才是数据所以第 i 条记录落在第 i1 行。Dim i, n, grid Set grid ScreenItems(MSFlexGrid1) n oRs.RecordCount grid.Clear grid.Rows n 6 2 行表头 n 行数据 3 行统计 1 行余量 grid.Cols 5 第 0 行整行合并做大标题 Dim z grid.Row 0 For z 0 To 4 grid.Col z grid.Text Computer Utilization Message (per Day) Next grid.MergeCells 4 grid.MergeRow(0) True 第 1 行列名 grid.TextMatrix(1, 0) NO. grid.TextMatrix(1, 1) Time grid.TextMatrix(1, 2) CPU% grid.TextMatrix(1, 3) RAM% grid.TextMatrix(1, 4) HardDisk% 第 2 行起填明细 If n 0 Then oRs.MoveFirst i 0 Do While Not oRs.EOF i i 1 grid.TextMatrix(i 1, 0) CStr(i) grid.TextMatrix(i 1, 1) CStr(oRs.Fields(0).Value) grid.TextMatrix(i 1, 2) CStr(oRs.Fields(1).Value) grid.TextMatrix(i 1, 3) CStr(oRs.Fields(2).Value) grid.TextMatrix(i 1, 4) CStr(oRs.Fields(3).Value) oRs.MoveNext Loop End If 底部三行统计位置跟着记录数走 grid.TextMatrix(n 3, 0) MAX grid.TextMatrix(n 4, 0) MIN grid.TextMatrix(n 5, 0) AVG资源原稿里在循环内写了n n 1而n同时在循环前被赋成RecordCount、在循环后又被写成记录数用于判断If n 0。一行变量担了三个角色只要记录数不为零循环跑完n就翻倍后面TextMatrix(n3, ...)直接越界轻则统计行跑到表格外面重则脚本抛错整块不刷新。这类问题在画面里没有堆栈只能靠看现象猜——表格有数据但底部三行不见了八成就是行号算错。统计值的取法也有讲究。原稿直接把AVG的结果塞进单元格显示出来是37.333333333333这种一长串。用Int(x * 1000 0.5) / 1000保留三位小数视觉上干净得多也不会因为四舍五入规则不同导致同一份数据两次查询显示不一致。5. 排查清单五类报错的现象、成因与处置5.1 数据链路写进去了但查不出来现象Management Studio 里能看到表在增长画面点查询却提示「没有找到符合条件的数据」换几个日期都一样。原因最常见的是库名或表名不一致。写入脚本连的是SQL-Report库、VBSReport表查询脚本连的是Example-report库、report表两段代码分别从不同来源抄来谁也没改。两个库都存在时不会报错只是查询永远命中空表。解决把连接串和 SQL 语句里的库名、表名全部搜一遍统一成实际建的那一套。更稳的做法是把库名、表名提成常量放在脚本开头改的时候只改一处。改完先跑一次SELECT COUNT(*)确认条数不为零再去看画面。现象查询范围放宽到一整年仍然只出几行。原因全局脚本的触发器没配或者配了但 WinCC 没进运行态。全局脚本只在项目激活后执行编辑器里点「编译」不会产生数据。解决看脚本编辑器里触发器那一栏是不是空的进运行态后等两个采集周期再查一次表。如果还是没有在action里加一句HMIRuntime.Trace把执行时间和错误码写进诊断窗口这是全局脚本唯一能用的排查手段。5.2 控件链路画面一打开就报错现象项目激活后画面打不开弹出「未注册的类」或者控件位置显示成一个白框。原因OCX 没注册或者注册到了错误的位置。64 位系统上把 32 位控件注册进system32WinCC 加载时会失败另一种情况是换了台机器只拷了项目文件没拷控件。解决确认进程位数32 位宿主就用SysWOW64目录加SysWOW64\regsvr32.exe注册。如果源文件都找不到从装过 VB6 的机器上复制MSFLXGRD.OCX和MSCOMCT2.OCXDTPicker 在这个文件里。现象控件能显示但点查询时脚本报「对象不支持该属性或方法」。原因画面里控件的Name被改过脚本里还在用默认名MSFlexGrid1或者控件被拖成了另一种类型属性集不一样。解决在画面编辑器里选中控件看属性面板顶部的名称跟脚本里的ScreenItems(...)逐字比对。大小写不敏感但拼写必须一致。5.3 显示链路数字对不上、表格错位现象明细行数和统计行的位置错开底部三行跑到表格中间去了。原因Rows设小了或者记录数和行号的换算关系写错。Rows一旦小于实际需要的行数TextMatrix写入越界会静默失败或抛错。解决先用固定行数Rows 100跑通填充逻辑确认数据落位正确再把行数改成动态计算。动态计算时记住公式是记录数 6标题 1 行、列名 1 行、数据 n 行、统计 3 行、余量 1 行。现象同一天的数据查询结果的时间列有的显示完整日期有的只显示时分。原因脚本里对「起止日期相同」做了特殊处理只截取时分显示。这个判断依赖两个日期控件的值完全相等如果其中一个带了时间部分判断就失效于是同一次查询里两种格式混着出现。解决要么统一显示完整时间要么在比较前把两个值都格式化成yyyy-mm-dd再比。不要直接比较日期型值那比的是毫秒级时间戳。5.4 边界条件那条最容易漏的记录现象每天的最后一条采集记录在日报表里时有时无。原因查询上界写成了23:59:59而 DATETIME 的精度是 3.33 毫秒落在23:59:59.5的记录被排除在外。采集周期是分钟级时这个问题不明显改成秒级或事件触发后立刻暴露。解决改用左闭右开区间上界取次日零点。日期控件只给到日次日零点用DateAdd(d, 1, ...)算出来拼接别手写。提示这套链路上任何一处「时有时无」的问题优先怀疑时间边界和区域设置这两个原因占了排查工作量的一大半而且都不报错。6. 导出 Excel 与打印把网格变成能签字的报表纸打印按钮的脚本逻辑是把 MSFlexGrid 里的内容逐格搬到 Excel 工作表加边框、调行高、设字体然后调PrintPreview或PrintOut。搬运本身没有技巧逐行逐列赋值即可真正决定成败的是后面几步格式设置。 画面脚本 - 打印按钮 OnClick Dim grid, xl, book, sheet, i, j, rows Set grid ScreenItems(MSFlexGrid1) Set xl CreateObject(Excel.Application) Set book xl.Workbooks.Add Set sheet book.Worksheets(1) xl.Visible True 调试期打开发布时改成 False rows grid.Rows For i 0 To rows - 1 For j 0 To grid.Cols - 1 Trim 去掉 MSFlexGrid 单元格尾部补的空格 sheet.Cells(i 1, j 1) Trim(grid.TextMatrix(i, j)) Next Next 四条边框1 到 4 分别是左、右、上、下 Dim k For k 1 To 4 sheet.Range(A1:E rows).Borders(k).Weight 2 Next sheet.Rows(1).RowHeight 0.75 / 0.035 行高单位是磅原稿这个换算是把厘米转磅 sheet.Rows(1).Font.Bold True sheet.Cells.EntireColumn.AutoFit 列宽自适应时间列不会被截断 sheet.PageSetup.CenterHorizontally True 水平居中布尔值比数值稳 sheet.PrintPreview 先预览确认后再换 PrintOut有三处值得说。Borders(k).Weight的 k 是固定枚举1 左、2 右、3 上、4 下漏掉哪个方向那一侧就没线打印出来像是表格被裁了一刀。PageSetup.CenterHorizontally原稿写的是2/0.035本质是拿一个换算后的数值当布尔用改成True更直白也不会因为 Office 版本差异被解释成别的含义。PrintPreview和PrintOut建议做成两个按钮或者加一个确认框——预览能让人发现列宽被截断、字体缺失这类问题直接送打印机的话纸就废了。还有一个隐性成本每次点打印都新开一个 Excel 进程连点五次就是五个 EXE 在后台挂着。发布前把xl.Quit和对象释放补上顺序是book.Close、xl.Quit、Set xl Nothing中间任何一步抛错进程都会残留。验证这套报表是否真的可用我一般跑三个用例选同一天看明细行数和统计行的位置对不对选跨月区间看月初月末那两条边界记录在不在选一个没有任何数据的日期看提示框是否正常弹出、表格是否被清空而不是留着上一次的结果。第三个用例最容易被跳过但它恰恰是现场操作员最容易触发的场景——选错日期、点了查询、画面还挂着昨天的数拿去交接就是事故。从那以后我每次接这类老项目都先把这三个用例跑一遍再谈画面美观。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑