
简介《Smartbi报表设置文档》面向正在使用 Smartbi 制作 Excel 风格报表的报表开发与数据分析人员聚焦模板引用、页面设置、隔行配色、列宽自适应、参数校验与参数排版、预览发布等常见配置环节适合需要快速上手或查漏补缺的初中级使用者。压缩包内共 1 个 docx 文件约 1.05MB以图文步骤和配置要点组织内容并附有列宽自适应、起止日期校验等宏脚本示例便于对照实际报表工程理解设置逻辑。目前已有 1304 人学习下载说明该主题具有一定的实践参考需求。读者可从中获得从模板设计到发布预览的完整设置脉络理解条件格式、AutoFit、参数排版及宏管理在 Smartbi 报表中的落点并借助代码片段排查列宽、参数联动和日期范围限制等问题提升报表搭建效率与规范性。1. 一份把 Smartbi 报表设置讲透的操作清单上个月帮一个财务口径的月报做返工问题不在 SQL也不在数据集全卡在展示层冻结窗格偏了一行、隔行变色在扩展后错位、用户把结束日期选到一年后一刷新直接把明细表拉出六位数行。这类事单看都不难难在每次都要重调一遍没人把步骤沉淀下来。这份 Smartbi 报表设置文档做的事很朴素——把引用模板、页面设置、隔行变色、列宽自适应、参数校验、参数排版、预览发布这七步固化成可照着敲的流程配套两段可直接复用的电子表格宏。它适合刚接手电子表格类报表的人也适合已经会做但每次都要翻旧报表抄样式的人。下面按我实际落地的顺序拆重点放在参数、代码和那些调试时才冒出来的坑。2. 引用模板与页面设置冻结 1-5 行背后的三个数2.1 从「模板」入口打开而不是新建空白Smartbi 的电子表格设计器是挂在 Excel 里的插件登录之后顶部会多出一个页签。多数人第一次用会直接新建空白工作簿结果数据集要重新绑、参数要重新建、连单元格的样式素材都得从零调。正确入口是页签里的「模板」从资源目录里挑一份结构接近的已有报表打开在它的基础上改样式。这样做的好处很实际数据集绑定、参数定义、已挂的宏、单元格的字体边框、甚至打印设置都会一起继承下来。我做季度报表时就是拿上一季度的模板改只动表头文案和取数条件二十分钟能出一版。代价是旧宏也会被继承。有一回打开模板后新写的校验宏不生效排查半天发现模板里本来就挂着一个同名的main后建的宏排在后面执行时被前者挡住。所以打开模板后的第一件事是去宏管理里把现有宏列一遍确认没有同名冲突再往下做。操作顺序记一下Excel 里点 Smartbi 页签登录账号。点「模板」在资源树里定位到目标报表双击打开。另存为新报表改名字、改存放目录避免直接改坏原模板。打开宏管理清点已有宏的名称和触发时机。第 3 步容易被跳过。直接改模板文件下次别人引用这份模板时,你的临时改动就跟着扩散出去了这种污染比写错一个公式更难查。2.2 页面设置首行空白行与冻结窗格的账原始文档里有一句注「第一行是空白行所以上面报表冻结的是 1-5 行」。这句话第一次看容易误解以为冻结 5 行就够了。实际账是这样的Excel 的行号从 1 开始第 1 行留白是为了给参数面板或者标题区留位置真正要固定的表头在第 2 到第 5 行。冻结窗格的操作落在第 6 行的「冻结拆分窗格」上效果是 1 到 5 行在浏览器端滚动时始终悬在顶部。为什么强调这个数因为电子表格发布到浏览器后滚动容器和 Excel 的视图不是一回事。冻结行数多一行底下内容区就少一行少一行表头滚走了用户就得往上翻。财务类报表一屏显示二十来行这一行的差别肉眼可见。页面设置里我一般会固定这么几项设置项建议值说明纸张方向横向列数超过 8 列基本都得横过来缩放调整为 1 页宽避免打印时右侧列被截断页边距上下 1.5cm左右 1cm留出装订和页码位置打印标题行$1:$5与冻结行保持一致页脚第 P 页 / 共 N 页多页报表必须有网格线打印时不显示靠单元格边框出线「打印标题行」这一项和冻结窗格是两套机制一个管屏幕一个管纸张很多人只设了前者导出 PDF 时第二页开始就没表头了。2.3 行列基准线先定骨架再填内容样式调整最忌讳上来就调字体颜色。我的习惯是先定三条基准线再谈美观第一条是行高。表头行固定 28 到 32 像素数据行 22 到 24 像素参数区单独一行 30 像素左右。行高不统一冻结区和非冻结区在滚动时会有明显的跳变感。第二条是列宽。文本类列客户名、科目名给到能放下 12 个汉字金额列统一宽度右对齐千分位日期列固定 10 个字符宽。列宽先手工定一个基准后面再用第 3 章的宏做自适应兜底顺序不能反——先自适应再手调等于白调。第三条是边框。外框一条粗线表头下缘一条中线数据区内部只留横线不留竖线。竖线一多密集数字看着就晕。这三条定完再去处理标题合并、字体字号、数字格式。数字格式里最容易翻车的是金额列用了「常规」导出后小数位数乱七八糟统一设成#,##0.00最稳。3. 隔行变色与列宽自适应条件格式与 autoFitColumns 宏3.1 条件格式做隔行变色公式写在哪一格隔行不同颜色的实现路径有两条一条是 Excel 的「套用表格格式」一条是条件格式配公式。电子表格场景我坚决用后者。套用表格格式会顺带改掉筛选按钮、边框和字体和模板里已有的样式打架而且它绑定的是一个区域对象单元格扩展之后区域边界不会自动跟着走。条件格式的做法是在「开始 → 条件格式 → 新建规则 → 使用公式确定要设置格式的单元格」里写MOD(ROW()-2,2)0这个公式的意思是当前行号减 2 之后能被 2 整除的行套用设定的填充色。为什么减 2 而不是直接MOD(ROW(),2)因为第 1 行是空白行第 2 行通常是表头真正的数据从第 3 行开始。用ROW()-2第 3 行算出 1奇数行不染色第 4 行算出 2偶数行染色。条纹就从数据首行开始不会出现「表头被染、第一条数据没染」的错位。应用范围要写大一点比如$A$3:$N$2000覆盖到扩展后的最大可能行数。这里有个细节电子表格的扩展单元格在渲染前条件格式只认设计态的区域扩展出来的行能不能继承格式取决于应用范围是否覆盖到了那些行号。范围给小了翻到第二页条纹就断了。填充色别选太扎眼。我一般用#F5F8FC这种接近白的浅蓝或者#FAFAFA浅灰打印出来几乎看不见屏幕上又能分清行。深色条纹在投影仪上会糊成一片。还有一条经验条件格式只设填充色不要在里面同时设边框和字体。多个规则叠加时优先级不好控边框经常被覆盖掉最后还得手工补。3.2 列宽自适应宏全局跑和单列调的两种写法手工拖列宽的问题是每次数据结构变一次就得重拖。原始文档给了一段通用宏我把它整理成带注释的版本function main(spreadsheetReport) { // 取第 0 个工作表多 sheet 报表建议改成按名称取见下方说明 var sheet spreadsheetReport.workbook.worksheets.get(0); if (sheet || sheet null) { return; } // maxDisplayRange 是渲染后的最大显示区域扩展单元格的行列也算在内 var counts sheet.cells.maxDisplayRange.columnCount; // 从第 0 列开始到 counts 列结束整体自适应 sheet.autoFitColumns(0, counts); // 只想调某一列时用单列版本索引从 0 开始第 4 列对应索引 3 // sheet.autoFitColumn(3); }逐段说一下逻辑。workbook.worksheets.get(0)拿的是工作簿里的第一张表索引从 0 起。单 sheet 报表这么写没问题多 sheet 的报表就有坑了——用户切到第二张表宏还在对第一张表做自适应看起来像没生效。稳妥的写法是先取getActiveSheet()或者按名称get(明细)定位再执行。maxDisplayRange.columnCount取的是当前渲染出来的最大列数。这里的「最大」是含扩展的也就是说数据集返回多少列它就报多少列不会只报设计态画好的那几列。这也解释了为什么宏要放在渲染之后触发渲染前取到的列数是设计态的会偏小自适应只覆盖了前几列后面的列还是原始宽度。autoFitColumns(0, counts)是区间版本起点 0、终点 counts。按原始文档的说法是「从 0 开始到 counts 列结束」终点给的是列数而不是最大索引多算一列通常无害空列自适应后还是原来的宽度。真要抠这个边界写成counts - 1也行效果上没有肉眼差别。autoFitColumn(3)是单列版本调试阶段特别有用。整表自适应一次要遍历所有列测量文本宽度列数上到三四十列、行数上万时页面会有明显的卡顿感。我的做法是先用单列版本把几个长文本列比如客户名称、备注单独调宽剩下的列手工定宽最后再决定要不要整体跑一遍。参数上还有两个可调的地方。一是自适应之后的最大宽度限制某些版本支持传入上限避免备注列被撑到半屏二是最小宽度防止全是数字的列被压成一条缝。这两个值没有统一标准按你们的字段实际情况试两三次就摸出来了。3.3 宏挂在哪、什么时候触发宏写完了不等于生效。电子表格的宏分客户端宏和服务器端宏列宽自适应这种依赖渲染结果的操作必须挂在客户端、在报表渲染完成之后执行。入口在 Excel 的 Smartbi 页签 → 宏管理 → 新建宏类型选客户端宏事件选渲染后或者首次加载后。触发时机的选择直接影响结果挂在「加载前」maxDisplayRange还是设计态的值自适应范围不全。挂在「渲染后」列数准确但如果报表有异步取数可能取数还没回来就执行了。挂在「刷新后」每次用户点查询都会重跑一次列宽跟着数据变观感更稳代价是每次刷新多一点耗时。我一般挂「渲染后 刷新后」两个时机前者保证首屏后者保证换参数之后列宽不塌。列数超过 40 列的报表只挂刷新后首屏用手工列宽顶着避免打开就卡。4. 参数联动校验重写 doRefresh 卡住 90 天边界4.1 为什么改 doRefresh 而不是监听参数事件参数校验有好几种做法在数据集 SQL 里加 where 条件、在参数面板上配校验规则、写宏拦截。前两种的边界能力有限——SQL 拦截会让用户看到一个数据库报错体验很差参数面板的规则配置对「两个日期之间的差值」这种跨参数计算支持不足。宏拦截的思路是报表每次刷新都会走spreadsheetReport.doRefresh把这个方法包一层在真正刷新之前做校验不通过就直接 return刷新根本不会发生。原始文档用的就是这个套路先把原函数存到_jhy_doRefresh再用一个新函数覆盖doRefresh校验通过后调用原函数。这个套路的妙处在于「无侵入」不用改数据集不用动参数定义一段宏搞定而且对所有触发刷新的入口都生效——点查询按钮、改参数回车、切页签全走同一条路。4.2 完整宏代码与逐行说明原始版本只做了两条校验我在实际项目里补了空值和解析失败的兜底否则用户清空日期直接点查询new Date()得到 Invalid Date比较结果恒为 false校验形同虚设function main(spreadsheetReport) { // 保存原始 doRefresh避免覆盖后无法刷新 if (!spreadsheetReport._jhy_doRefresh) { spreadsheetReport._jhy_doRefresh spreadsheetReport.doRefresh; } spreadsheetReport.doRefresh function (fromButton, delayMask) { // 参数名必须和参数面板上的完全一致含空格和数字后缀 var endRaw spreadsheetReport.getParameterValue(结束日期 2); var startRaw spreadsheetReport.getParameterValue(开始日期 2); // 任一为空时放行交给数据集或后端兜底避免空值把校验卡死 if (!endRaw || !startRaw) { this._jhy_doRefresh(fromButton, delayMask); return; } // 把 - 换成 /兼容部分浏览器对 yyyy-MM-dd 的解析差异 var endtime new Date(String(endRaw).replace(/\-/g, /)); var starttime new Date(String(startRaw).replace(/\-/g, /)); // 解析失败不要静默放行直接提示否则用户以为查询成功了 if (isNaN(endtime.getTime()) || isNaN(starttime.getTime())) { setTimeout(function () { alert(日期格式无法识别请重新选择); }, 100); return; } // 结束日期不早于开始日期 if (endtime starttime) { setTimeout(function () { alert(结束时间不能早于起始时间); }, 100); return; } // 跨度不超过 90 天等于 90 天放行超过才拦 if (endtime.getTime() - starttime.getTime() 1000 * 3600 * 24 * 90) { setTimeout(function () { alert(两个参数的差不能超过 90 天); }, 100); return; } // 校验全部通过走原始刷新逻辑 this._jhy_doRefresh(fromButton, delayMask); }; }逻辑上有三个关键点。第一是_jhy_doRefresh这个暂存。doRefresh是实例方法直接覆盖会把原实现弄丢之后再也刷不出来。先用一个带前缀的私有属性存一份判断if (!spreadsheetReport._jhy_doRefresh)是为了防止宏被重复执行时把已经包装过的函数又存一遍套娃下去会栈溢出。第二是setTimeout(..., 100)包住 alert。看起来多此一举实际是因为 alert 是阻塞式弹窗在刷新流程里同步弹出会打断上层调用链某些版本下表现为「弹了框但按钮一直转圈」或者「弹完框整个表格白了」。放到定时器里让刷新流程先退出弹窗在下一个事件循环里出来就不会卡死。100 毫秒是个经验值太短可能赶在流程结束前太长用户会觉得点了没反应。第三是 90 天的算法。endtime.getTime() - starttime.getTime()得到毫秒差除以1000 * 3600 * 24就是天数。这里没有做「日期归一化」也就是说 1 月 1 日 00:00 到 4 月 1 日 00:00 是整 90 天放行到 4 月 1 日 00:00 零 1 秒就超了。如果参数控件只到日期粒度这个精度足够如果带时分秒得先把两端截断到当天零点再比否则用户会看到一个莫名其妙的「差一天」。国内没有夏令时不用担心某一天只有 23 小时的问题但跨时区部署的服务器要留意前端new Date解析的是本地时区。4.3 参数名、空值与日期格式的三个细节参数名这一条值得单独说。原始文档里的参数名是「结束日期 2」和「开始日期 2」中间有空格后面有数字后缀。这个后缀是参数面板复制粘贴时自动加的很多人写宏的时候凭记忆敲成「结束日期」getParameterValue返回 undefined后面new Date(undefined)直接炸但因为是异步流程控制台报错一闪而过表面看起来就是「宏没反应」。排查方法很简单在宏的第一行加一句console.log(spreadsheetReport.getParameterValue(结束日期 2))打开浏览器开发者工具看输出。返回null或undefined就是名字不对返回日期字符串就是名字对了。这个习惯我保持了挺久比猜快得多。空值处理没有出现在原始文档里但真实场景一定会遇到。用户把日期清空参数传上来是空字符串replace不会报错new Date()得到 Invalid DateInvalid Date 某日期恒为 false于是两条校验全部放行查询照跑数据全量返回。所以空值必须显式判断要么放行要么拦截不能留给运气。日期格式方面参数面板传上来的通常是yyyy-MM-dd字符串。用replace(/\-/g, /)转成yyyy/MM/dd再交给new Date是为了绕开部分浏览器把带横线的日期当 UTC 解析的差异——同一个日期字符串一处按 UTC 算一处按本地算差出八小时跨月边界上就会误判。换成斜杠之后主流实现都按本地时间解析行为一致。5. 避坑与排查六条踩过的记录5.1 宏保存了但不生效刷新毫无反应现象宏管理里能看到新写的宏保存也成功了但点查询按钮走的是原始刷新逻辑alert 一次都没弹。原因通常有三个宏的类型选错写成了服务器端宏而校验需要在前端刷新链路上拦截宏的事件没勾创建时只填了名字和代码事件下拉框留空模板本身已经挂了一个同名的main两个宏冲突执行顺序靠前的那个赢。解决先确认宏类型是客户端宏、事件至少勾一个「刷新前」或「加载后」再去宏管理里把同名宏找出来要么删掉旧的要么给新的换个名字并在旧宏里显式调用最后在代码首行加日志打开开发者工具确认宏真的被执行了再往下查逻辑。5.2 列宽自适应后合并单元格被压扁现象跑完自适应宏表头那几列合并的大标题变成了窄窄一条文字挤成两三行或者干脆被截断。原因autoFitColumns的测量对象是单元格里的文本合并单元格只按左上角那个单元格的内容算宽度合并区域的其他列拿不到内容被当成空列压到最小宽度。整表自适应时这种情况特别常见。解决把合并单元格所在的列排除在自适应范围之外。常见做法是先跑autoFitColumns再手工把合并区的列宽设回固定值或者反过来只对数据列区间做自适应比如从第 3 列开始表头列全部手工定宽。我的习惯是后者代码里把起点从 0 改成实际的第一个数据列索引。5.3 冻结窗格在浏览器里偏了一行现象Excel 里预览时表头固定得好好的发布到浏览器后冻结区把第一条数据也冻住了滚动的时候第一条数据永远悬在表头下面。原因冻结位置是按「当前选中单元格上方」计算的。在 Excel 里点第 6 行冻结冻的是 1 到 5 行如果点的是第 7 行冻的就是 1 到 6 行。设计过程中插入或删除过行选中位置跟着漂移冻结行数就悄悄变了。首行空白行让这个偏移更隐蔽。解决发布前重新点一次目标行确认冻结线在哪Excel 的「视图 → 冻结窗格」菜单里能看到当前状态发布后在浏览器里滚动到底部再滚回来观察表头和第一条数据是否分离。这个检查我一般放在发布清单的第一项三十秒能做完。5.4 隔行变色到第二页就断了现象首屏条纹正常往下翻几页之后条纹消失或者变成整片同色再往下又出现。原因条件格式的应用范围只覆盖了设计态的固定行数比如设到$A$3:$N$50第 51 行开始就不在规则里了。扩展单元格渲染出来的行号超出了这个范围格式不继承。解决把应用范围放大到远超过预期数据量比如$A$3:$N$5000同时确认条件格式的公式用的是相对行号ROW()而不是绝对引用ROW($A$3)后者会让所有行都按第 3 行的结果染色条纹直接消失。范围放大对性能影响很小条件格式是渲染时按行判断的不预计算。5.5 日期校验对某些用户永远放行现象同一个报表A 用户选超长区间会被拦B 用户选同样的区间直接查出来了。原因new Date(2024-01-01)在不同浏览器内核下的解析行为不一致有的按 UTC有的按本地时间极少数返回 Invalid Date。一旦解析失败比较结果恒为 false两条校验全部失效。另外如果参数控件允许手工输入用户敲了「2024年1月1日」这种格式replace之后仍然解析不了。解决统一用yyyy/MM/dd格式解析解析后立刻用isNaN(date.getTime())判一次失败就弹提示并阻断刷新绝不放行。参数控件尽量设成日期选择器禁用自由文本输入。如果业务上必须支持多格式就写一个小的解析函数按yyyy/MM/dd、yyyy-MM-dd、yyyyMMdd依次尝试全失败再提示。5.6 参数区一行塞了七个换行后第二行只剩一个现象参数面板在宽屏上看着还行一到笔记本分辨率或者浏览器缩放 125%筛选项自动折行第二行孤零零挂着一个「查询」按钮视觉上很难看。原因参数排版的容器宽度是自适应的设计时按大屏摆的六个一行实际渲染宽度不够就折行折行位置不受控。解决控制每行参数个数不超过 6 个并且按「标签 控件」算一个整体不要只数控件。真正折成两行时第二行至少保留 2 个避免孤项。更稳的做法是把筛选项整体左对齐、宽度用百分比而不是固定像素让它在 1280 到 1920 之间都能均匀分布。6. 参数排版与发布前验证上线前跑完这张表6.1 参数排版的分配规则参数区的排版原则原始文档写得很克制筛选项居左每行不超过 6 个如果排成两行第二行至少保留 2 个。这三条背后是视觉平衡不是随便定的数。每行 6 个是上限超过之后单个控件的可用宽度会被压缩日期选择器这种带图标的控件最先变形日历图标被挤到框外。第二行至少 2 个是为了避免「7 个参数排成 61」这种孤项布局孤零零一个按钮挂在左边用户视线会被拽过去反而找不到主查询按钮。我一般按重要性排序把最常用的两三个参数放第一行左侧日期区间放第一行右侧次要的维度筛选放第二行。参数多到超过 12 个就该考虑折叠面板或者默认值隐藏了硬塞进去只会让首屏全是控件、看不到数据。6.2 发布前跑一遍的验证清单预览发布不是点一下按钮就完事。下面这张表是我每次上线前必跑的顺序从样式到逻辑再到端上表现检查项操作通过标准冻结窗格浏览器里滚到底再滚回顶表头 1-5 行始终可见第一条数据能滚走隔行变色翻到数据末页条纹连续表头不被染色列宽换一组返回列数不同的参数列宽随内容调整合并单元格不变形参数校验结束日期早于开始日期弹提示不刷新表格保持原数据参数校验区间正好 90 天正常刷新不拦截参数校验区间 91 天弹提示不刷新参数校验清空任一日期后查询按约定放行或拦截不出现全量拉取参数排版浏览器缩放到 100% 和 125% 各看一次无孤项控件不变形导出导出 Excel 和 PDF 各一份表头重复、页码正确、金额小数位一致移动端手机浏览器打开参数区可横向滚动表格不溢出90 天这个边界最容易漏测。开发时随手选两个日期差值不是 3 天就是 300 天正好卡在 90 天的那一次往往没人试结果上线第一天就有用户反馈「差一天也被拦了」或者「91 天居然放行了」。我现在的习惯是边界值单独列一行等于、小于、大于各跑一次跑完再点发布。还有个小技巧这段校验逻辑不止一个报表要用与其每份报表复制一遍宏不如把日期解析和区间判断抽成一个公共函数放在同一个宏文件里各报表的main只负责取参数和调函数。这样 90 天改成 60 天时只改一处不用满资源目录找副本。宏管理支持多个宏共存公共函数那个宏不挂事件、只提供函数定义就行。吃过一次亏之后我每次点发布前都会强制走一遍上面那张表哪怕这次只改了一个表头文字。听起来像形式主义但真正救过我的一次是改文案时不小心把冻结行往下拖了一行表格里完全看不出来列表里滚一下才发现表头少固定了一行。希望帮到你。本文还有配套的精品资源点击获取