简介:面向使用Smartbi进行电子表格报表开发的业务人员与数据分析师,这份文档系统整理了报表从搭建到发布的完整设置思路,可帮助初学者快速上手、也为熟手提供查漏补缺的参考。压缩包内共1个docx文件,约1.05MB,以图文步骤形式组织内容。文档围绕引用模板、页面设置、隔行不同颜色、列宽自适应、参数设置、参数排版与预览发布等环节展开,其中列宽自适应给出可全局或单列调用的通用宏代码,参数校验部分则提供结束日期不早于开始日期且跨度不超过90天的宏实现,并附参数排版中每行数量分配的经验说明。目前已有1302人学习,适合需要规范报表样式、补充宏脚本写法的读者对照实践。
1. 一份能直接抄的 Smartbi 报表设置清单:从模板引用到参数校验
做报表的人大概都有过这种体验:样式调了三小时,发布出去被业务一句“列宽挤成一坨”打回;参数明明填了开始和结束日期,用户偏要选个倒序区间,后台直接跑出个空结果。这份 Smartbi 报表设置文档解决的正是这类落地问题——它不讲产品架构,只把 Excel 插件里从引用模板、页面设置、隔行变色、列宽自适应,到参数联动校验、参数排版、预览发布的动作串成一条可复现的线。适合已经能登录 Smartbi、要在 Excel 里交付第一张正式报表的从业者,也适合被样式和参数校验反复折腾过的熟手拿来对照。下面按我实际拆这套流程的顺序写,代码和参数都能直接改。
2. 模板引用与页面设置:先把骨架定死再谈样式
报表翻车十有八九不是数据错,是骨架没定好就开始调样式。模板引用和页面设置这两步看着最没技术含量,恰恰决定了后面隔行变色、冻结行、列宽自适应能不能稳定生效。我一般把这两步当成“地基”处理,地基歪了,后面每一处美化都是在补窟窿。
2.1 引用模板:为什么必须在 Excel 页签里登录再打开
Smartbi 的报表设计入口挂在 Excel 的 Smartbi 页签上,不是在浏览器里点出来的。正确顺序是:打开 Excel,切到 Smartbi 页签,先完成登录,再点“模板”打开目标模板,然后在这张模板上设计新报表样式。这个顺序不能反——没登录就点模板,插件拿不到会话,模板可能以只读或脱机状态打开,你改的样式保存不回服务端。
常见做法是先在模板库里挑一张结构最接近的模板做底,而不是从空白表起。原因是模板里已经带好了数据集绑定、参数定义和单元格扩展属性,你在它上面改样式,等于复用了别人调好的数据链路,只动表现层。从空白表起不是不行,但参数、数据集、扩展方向都得自己重新挂,出错面大得多。
这里有个容易被忽略的点:模板打开后,先确认当前工作表是不是你要设计的那一张。一个模板文件里常有多张 sheet,插件默认操作的是活动工作表,样式改错 sheet 是血泪经验里排前三的翻车原因。确认完再动手,别急着填数。
2.2 页面设置:冻结行为什么要按 1-5 行算
页面设置里最常被问的是冻结行。文档里给了一个很具体的注:第一行是空白行,所以报表冻结的是 1-5 行。这句话的信息量在于——冻结范围要按“实际可见表头占几行”来定,而不是拍脑袋填个数字。
假设你的报表结构是:第 1 行留空做视觉留白,第 2 行大标题,第 3 行副标题或筛选说明,第 4 行列头,第 5 行是列头的第二层(比如分组维度)。那么冻结到第 5 行,滚动时这五行始终钉在顶部,用户往下翻数据不会丢失列语义。如果你只冻结到第 4 行,第二层列头会跟着滚走,多级表头就散了。
操作上,在页面设置里把冻结窗格定位到第 6 行第 1 列(即冻结前 5 行),等价于“冻结 1-5 行”。判断标准很简单:把所有表头行数出来,冻结行数 = 表头总行数。空白行如果承担了视觉分隔作用,也算进表头区,否则滚动时留白消失,标题会贴到数据上,观感很差。
页面设置里还要顺手确认打印区域和缩放。报表最终常要导出 PDF 或打印,缩放没设好,宽表会被截断成两页,列宽自适应做得再好也白搭。我一般先把打印区域框到数据实际范围,缩放选“调整为 1 页宽”,再去做后面的列宽处理。
2.3 隔行不同颜色:条件格式比手刷靠谱
隔行变色这件事,手工一行行填背景色是最容易返工的。数据行数一变,颜色就错位。正确做法是用条件格式按行号取模判断,让颜色跟着行号走,而不是跟着你刷的那几行走。
在 Excel 里选中数据区,新建条件格式规则,用公式判断当前行是否为偶数行,成立则填浅色底。公式思路是MOD(ROW(),2)=0这类行号取模,具体写到哪个单元格取决于你选区左上角的位置。关键是公式里的行引用要用相对行,别锁成绝对引用,否则整片区域只会按第一行的奇偶统一上色,看起来像没生效。
为什么强调用条件格式而不是直接刷色?因为报表数据是动态扩展的,今天 20 行明天 200 行,条件格式会随行数自动铺开,手刷的颜色只覆盖你刷过的那一段。另外条件格式的规则优先级要留意,如果同一区域还有别的底色规则(比如异常值标红),隔行色的优先级要排在后面,否则会把业务高亮盖掉。这一点在多层规则叠加时特别容易踩,调的时候把规则管理器打开,按“先特殊后通用”的顺序排。
3. 列宽自适应与参数校验:两段能直接抄的宏
样式骨架定完,接下来是这份文档里最有复用价值的部分:列宽自适应和日期参数校验。两段都是挂在 Smartbi 宏管理里的 JavaScript,不是 VBA,别搞混。宏的入口在 Excel 的 Smartbi 页签下“宏管理”,新建宏后把代码贴进main函数即可。下面逐段拆。
3.1 列宽自适应:全局与单列两种写法
先看全局自适应的代码:
function main(spreadsheetReport) { var sheet = spreadsheetReport.workbook.worksheets.get(0); if (sheet === "" || sheet === null) { return; } var counts = sheet.cells.maxDisplayRange.columnCount; // 获取电子表格列数 sheet.autoFitColumns(0, counts); // 从 0 开始,到 counts 列结束 // sheet.autoFitColumn(3); // 只对某列进行自适应,第一列为 0 }逻辑说明:worksheets.get(0)取的是第一张工作表,索引从 0 开始,这点和很多 Excel 脚本习惯一致。maxDisplayRange.columnCount拿到当前显示范围的最大列数,注意是“显示范围”不是“数据范围”,如果表里有隐藏列或空白占位列,这个值可能比你预期的略大,自适应会多处理几列,通常无害。autoFitColumns(0, counts)从第 0 列一直处理到 counts 列,把整片区域的列宽按内容撑开。
参数怎么改:如果你只想对某一列自适应,把全局那行注释掉,启用autoFitColumn(3),括号里的数字是列索引,第一列是 0,所以 3 代表第四列。调试阶段我一般先单列试,确认索引对得上再放开全局,避免一上来整表重排看不出是哪列出的问题。
这里有个边界要提醒:列宽自适应在数据量很大时会有性能开销,因为它要逐列测量内容宽度。报表列数上百、行数上万时,全局自适应可能让预览明显变慢。常见做法是只对关键文本列(名称、描述类)做自适应,数值列给固定宽度,既好看又快。文档里说这段代码是通用的,通用指的是写法通用,不代表任何规模都该无脑全局跑。
3.2 日期参数校验:拦截倒序区间和超 90 天跨度
第二段宏解决的是参数层面的业务约束:结束日期不早于开始日期,且两者跨度不超过 90 天。代码通过重写doRefresh来在刷新前做校验:
function main(spreadsheetReport) { if (!spreadsheetReport._jhy_doRefresh) { spreadsheetReport._jhy_doRefresh = spreadsheetReport.doRefresh; } spreadsheetReport.doRefresh = function(fromButton, delayMask) { // 根据参数名称获取参数值 var endtime = new Date(spreadsheetReport.getParameterValue("结束日期 2").replace(/\-/g, "/")); var starttime = new Date(spreadsheetReport.getParameterValue("开始日期 2").replace(/\-/g, "/")); if (endtime < starttime) { setTimeout(function() { alert("结束时间不能早于起始时间"); }, 100); return; } // 结束日期不大于开始日期 90 天 if (endtime.getTime() - starttime.getTime() > 1000 * 3600 * 24 * 90) { setTimeout(function() { alert("两个参数的差不能超过 90 天"); }, 100); return; } this._jhy_doRefresh(fromButton, delayMask); }; }逻辑说明分三层。第一层是“保存原函数”:把原始的doRefresh存到_jhy_doRefresh上,这是典型的装饰器思路,先备份再覆盖,保证校验通过后还能调回原逻辑,不会把刷新能力弄丢。第二层是“取值并转日期”:getParameterValue按参数名拿值,参数名必须和报表里定义的完全一致,这里写的是“结束日期 2”和“开始日期 2”,你实际报表里叫什么就改成什么,名字对不上会拿到空值,new Date解析出 Invalid Date,后面的比较全部失效。replace(/\-/g, "/")是把2024-01-01转成2024/01/01,因为部分环境下横杠格式的日期解析不稳定,斜杠格式兼容性更好,这是踩过坑之后的固定动作。第三层是“校验失败就 return”:不调原刷新,直接弹提示,等于把这次刷新拦下来。
参数怎么改:90 天这个阈值写在1000 * 3600 * 24 * 90里,1000 毫秒乘 3600 秒乘 24 小时得到一天的毫秒数,再乘 90。要改成 30 天就把最后的 90 换成 30,别去动前面的换算。setTimeout包 100 毫秒再 alert,是为了让提示框在页面渲染稳定后弹出,直接同步 alert 在某些浏览器里会被后续刷新覆盖掉,这个延迟是经验值,不用改。
注意:这段校验依赖参数确实存在且格式为日期。如果报表参数是可选的、允许为空,getParameterValue返回空串时new Date("")是 Invalid Date,比较结果恒为 false,校验会被绕过。需要强制校验的场景,得在取值后先判空再比较,这一点文档没展开,但实际配可选参数时绕不过去。
3.3 宏挂载位置与生效时机
两段宏都通过 Smartbi 页签下的“宏管理”新建。挂载时要注意作用域:宏是挂在当前报表上的,换一张报表要重新挂,不是全局生效。生效时机是报表刷新或预览时触发main,所以改完宏必须重新预览一次才能验证,光保存不预览等于没测。
调试期建议在宏里临时加console.log打印取到的参数值,确认参数名和格式都对,再删掉日志。直接靠 alert 排查效率低,因为 alert 会打断流程,看不清中间值。控制台能看到每次刷新的实际入参,定位“参数名写错”这类问题快得多。
4. 参数排版与预览发布:交付前必须过的三道关
样式和校验都通了,最后一步是把参数区排好看、把报表发出去。这一步最考验“具体问题具体分析”,因为参数数量、名称长度、业务习惯每家都不一样,没有一套排版能通吃。文档给的规则是筛选项居左、每行尽量平均分配、一行不超过 6 个、两行时第二行至少保留 2 个。这几条背后其实是在控制视觉重心和换行节奏。
4.1 参数排版的分配规则怎么落地
“筛选项居左”是因为用户扫视参数区是从左往右读,把最常用的筛选放左边符合操作习惯。一行不超过 6 个,是因为超过 6 个之后每个参数的标签加输入框会被压得很窄,日期选择器这类控件会挤到换行,反而更难用。两行时第二行至少 2 个,是为了避免出现“第一行 6 个、第二行孤零零 1 个”的失衡布局,视觉上会显得头重脚轻。
落地时我一般先数参数总数,再按 6 的上限分组。比如 8 个参数,分成 4+4 比 6+2 更均衡;9 个参数分成 5+4 或 4+5,别做成 6+3。参数名特别长的(比如带“统计口径”字样的),单独给它更宽的格位,不要硬塞进平均分配里,否则标签会被截断成省略号,用户根本不知道筛的是什么。这一步没有代码,靠的是在预览里反复看,调到一眼能读全为准。
4.2 预览发布前要核对的清单
预览不是点一下看有没有报错就完事。我习惯按这几项过一遍:冻结行滚动后表头是否还在、隔行色在数据扩展后是否连续、列宽自适应后有没有列被撑得过宽或过窄、参数校验在倒序和超 90 天两种非法输入下是否都能拦住、参数区在常见分辨率下是否换行错乱。任何一项不过,回到对应章节改,别带着问题发布。
发布时注意区分“预览”和“正式发布”的差异:预览用的是当前设计态,发布后走的是服务端渲染,个别样式(尤其是条件格式和宏触发的行为)在两端表现可能不完全一致。稳妥做法是发布后从报表入口重新打开一次,按真实用户路径点一遍参数、翻一遍数据,确认和预览一致再交付。
5. 这套流程的边界:哪些坑我替你踩过了
上面按顺序走下来,大部分报表能顺利交付。但有几处边界,文档没写透,我实际配的时候翻过车,这里按“现象 → 原因 → 解决”列出来,你对照着排。
现象一:列宽自适应跑了,但某几列还是挤在一起。原因通常是这些列里有合并单元格,autoFitColumns对合并区域的测量不准,会按合并前的内容宽度算。解决办法是把合并单元格拆开,或者对这几列单独用autoFitColumn指定一个经验宽度,别指望全局自适应兜底。
现象二:日期校验弹了提示,但报表还是刷新了。原因是doRefresh被别处又覆盖了一次,或者宏挂载顺序导致你的装饰器没生效。排查方法是确认宏管理里只有这一处重写doRefresh,多个宏同时改同一个方法会互相覆盖,后挂的把先挂的顶掉。解决就是合并到一个宏里,别分散。
现象三:参数名带空格或特殊字符,getParameterValue取不到值。现象是校验永远不触发,因为拿到的是空。原因是参数名必须逐字符匹配,多一个空格都不行。解决是把参数名从报表参数定义里复制出来,别手敲。
现象四:隔行变色在导出 PDF 后消失。原因是部分导出通道不渲染条件格式,只认静态底色。如果交付物必须带隔行色且要导出,得在导出前把条件格式结果转成静态填充,或者确认导出通道支持条件格式。这个坑比较隐蔽,预览里一切正常,导出才发现。
现象五:90 天校验在跨月边界误判。原因是直接用毫秒差比较,遇到夏令时或时区偏移的日子,一天可能不是精确的 86400000 毫秒。常见做法是把两个日期都归一到当天零点再算差值,或者用日期部分做比较,避开时分秒和时区干扰。
这些坑的共同点是:预览阶段不一定暴露,往往在真实数据量、真实导出、真实用户操作下才冒出来。所以发布前的核对清单不能省,尤其是非法输入和导出这两项。
6. 进阶:把校验逻辑抽成可复用宏,并用边界用例验证
走到这里,单张报表已经能交付。但如果一个项目里有十几张报表都要做同样的日期校验,每张复制一遍代码,改参数名改到怀疑人生,维护成本会很高。我的做法是把校验逻辑抽成一个带参数的通用函数,每张报表的宏只负责传参数名和阈值,逻辑本体只维护一份。
// 通用日期区间校验,参数名和天数阈值由调用方传入 function validateDateRange(report, startParam, endParam, maxDays) { var startRaw = report.getParameterValue(startParam); var endRaw = report.getParameterValue(endParam); // 可选参数为空时跳过校验,强制校验场景去掉这段 if (!startRaw || !endRaw) { return true; } var start = new Date(startRaw.replace(/\-/g, "/")); var end = new Date(endRaw.replace(/\-/g, "/")); if (isNaN(start.getTime()) || isNaN(end.getTime())) { return true; // 格式异常交给参数控件自身约束 } if (end < start) { setTimeout(function() { alert("结束时间不能早于起始时间"); }, 100); return false; } if (end.getTime() - start.getTime() > 1000 * 3600 * 24 * maxDays) { setTimeout(function() { alert("两个参数的差不能超过 " + maxDays + " 天"); }, 100); return false; } return true; }这段把参数名和天数都提成入参,maxDays让不同报表用不同阈值,提示语里的天数也跟着变,不用再手改字符串。判空和isNaN两道前置检查,是为了让可选参数和格式异常不至于把校验逻辑带崩。调用侧只需要在doRefresh装饰器里写一行if (!validateDateRange(...)) return;,各报表的差异收敛到参数名上。
抽出来之后,验证方式也要跟着变。我一般准备四组边界用例:正常区间(应通过)、倒序区间(应拦截)、恰好 90 天(应通过,因为判断是大于不是大于等于)、91 天(应拦截)。这四组跑通,基本能确认阈值和比较符号没写反。很多人栽在“大于”和“大于等于”上,90 天整被误拦,业务方会立刻反馈。
还有一点值得说:宏里的alert在正式环境体验一般,用户点掉提示后参数还停在非法值上,得手动改。更顺的做法是把提示做成页面内的文字提示,但这依赖报表模板里预留提示位,属于设计阶段就要考虑的事。如果模板已经定死,alert 是最低成本的兜底,先保证拦得住,再谈体验。
从那以后我每次配日期参数,都强制先把这四组边界用例在预览里跑一遍,再发布。校验逻辑这种东西,正常路径永远看不出问题,只有边界才暴露。希望帮到你。
本文还有配套的精品资源,点击获取