ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Smartforms复杂表格布局利器:Template与Loop组合实战

Smartforms复杂表格布局利器:Template与Loop组合实战 做SMARTFORMS这些年如果只能留一个绝活我肯定选Template加Loop的组合拳。标题里这两个词看着简单但真正把复杂表格布局做利索的人不多。很多同事一提到Smartforms就头疼觉得这玩意图形化界面别扭、调格式靠运气、出了问题难排查。其实反过来想Smartforms在SAP生态里已经活了二十多年能批量套打、能调用ABAP逻辑、能处理成千上万行明细稳定性和灵活性依然是很多新式打印方案比不了的。这篇文章就是专门聊Template和Loop怎么配合使用帮你在Smartforms里搞定那些看着就发怵的复杂表格布局。所谓复杂无非几种情况列数多、行数多、行内又分区块、跨页还得带表头。这些场景用Template画骨架、用Loop填充数据再搭配几个不常被人提起的设置项基本都能稳稳拿下。文章里也会把我在项目里踩过的坑、试出来的土办法一并倒出来适合正在被Smartforms折磨的ABAP开发也适合刚接触表单打印、想少走弯路的初学者。1. 核心思路拆解Template和Loop究竟是干什么的1.1 两个概念的各自分工Template在Smartforms里是一个页面上的图形区域你可以把它想象成一张Excel表格的底版先画好几行几列、定好列宽行高、设置好边框线。这张底版是静态的也就是说不管数据来多少它的结构不会变。比较适合固定列数的表头区、固定行数的摘要区以及在主表区上面需要并排展示的一组字段。Loop则是数据驱动循环。你在Template画好的某一个区域内指定一个内表Smartforms会按内表的行数自动重复这一段区域。Loop和Template搭配的方式通常是在Template里画出一行的“行模板”然后在这行外面包一个Loop数据有多少行这行模板就垂直复制多少次。这就是最经典的“模板行数据循环”的组合布局。1.2 为什么这两个组合能解决复杂表格问题很多复杂表格本质上是在二维平面里既有固定的行和列又有动态增长的数据区域。如果完全用静态文本和图形拼数据一多变不了如果全部丢给表格控件Table Control处理又很难做到单元格合并、跨行显示自定义文本、特定条件整行高亮这类精细控制。TemplateLoop的妙处在于两层分离Template负责“位置和样子”Loop负责“数量和内容”。你要做的就是把固定部分抽出来画成模板把动态部分包进循环中间用字段映射来关联数据。这个逻辑一旦想清楚不管表格多复杂都能拆成“静态骨架”加“动态行”两个部分。1.3 适合与不适合用这个组合的场景先说适合的。多栏明细账、订单行项目、送货单、带有固定头尾和中间明细的各类套打单据这些典型场景都能用TemplateLoop做得又快又稳。不太适合的也有几类需要单元格跨页拆分显示的长文本比如整段打印合同条款这种用Template逐行拼会很痛苦需要自由流式排版、文字自然换行的内容直接在主窗口用文本节点更合适还有那种结构完全不确定、每行字段数量不同的动态报表也不适合套死模板。我自己定过一条判断标准如果表格的长宽比例和行列结构在需求阶段就已经固定了那必然是用Template只要行数不确定、列结构确定那就用Loop包住行模板。两条同时成立组合方案就是最优解。2. Template布局设计的实操要点2.1 在Smartforms里创建Template的步骤进入Smartforms事务码SMARTFORMS创建或打开一个表单后在“页面”节点下新建一个页面然后在页面的“图形”区域右键添加“模板”节点。编辑器会弹出一个类似画布的区域初始状态给你一个默认大小的矩形框。在设计时有一个关键习惯我强烈建议养成先把模板放在一个独立的Main Window里而不是直接放在页面顶层。这样做的好处是Main Window可以控制分页行为当Loop数据多到一页放不下时Smartforms会自动把剩余内容推进下一页这个机制直接决定跨页是否正常。在模板内部用右键可以对矩形框进行拆分。水平拆分生成多列垂直拆分生成多行。拆分后每一个单元格都可以单独设置高度、宽度、边框线、对齐方式和字体属性。布局设计阶段多花十分钟把行列画对后面数据绑定就能节省一小时。2.2 单元格合并和特殊表头的处理方法复杂表格里经常需要合并单元格比如表头跨两列、左边一列跨三行做分组标题。Smartforms模板编辑器支持单元格合并操作方式是选中需要合并的多个单元格后从右键菜单选“组合单元格”。合并之后数据绑定时以合并后的总单元格作为整体来引用。有一种情况要注意合并单元格后如果这个合并区域还要放进Loop里循环展开那Loop的高度基准就得用合并区域的总高度而不是单行高度。我在早期踩过一次表头合并了两行但循环时行的参照高度只取了第一行的高度结果每行内容都往上挤整个表格彻底错位。解决办法是在模板里把两行合并后再在合并单元格的属性里指定固定的最小高度。2.3 边框线、行高与Main Area的匹配边框线的设置藏在每一行或单元格的“边框”属性里。下拉框有“无边框”“只显示上下”“四边全显示”等选项。做表格时最容易出现的坑是模板的外边框和Loop内部行的内边框叠加后打印出来出现双重粗线。应对方案是最外层的模板只画外边框每一行模板只画下边框或只画水平线竖线靠每一列的左边框来呈现这样互相不叠加。Main Area的设置同样不能忽略。每个Main Window高度是固定的如果模板总高度、Loop单行高度的乘积加总后超过了Main Area高度Smartforms不会主动帮你压缩而是会把超出的行截断或直接导致分页异常。所以设计前要算好数据行数乘以行高是否在控制范围内必要时为Main Area单独创建多个页面类型Next Page让数据量大的情况自动续页。2.4 模板的命名规范和维护习惯很多项目里表单节点名称都是默认的比如TEMPLATE_1、LOOP_1过三个月回来看就跟看天书一样。我自己的习惯是模板节点一定写成结构语义名例如TMPL_ORDER_HEADER、TMPL_ITEM_ROWLoop节点写成LOOP_ITEM_DATA。命名的意义在于字段绑定、调试和后续交接时你知道自己在操作哪个区域。还有一个小习惯是给每个Loop的“命令”子节点写注释。点击Loop能看到子节点里有一个“命令”这里可以插入ABAP逻辑片段。注释写在代码上方其他同事打开表单时能快速知道这个循环的用途和数据源。别嫌这繁琐项目后期维护的时候注释比任何文档都管用。3. Loop循环绑定与数据驱动的关键设置3.1 Loop节点的创建和数据源指定在已经画好的模板行区域上右键选择“循环”Smartforms会为这一行包上一个Loop节点。双击Loop打开属性面板最重要的设置是“数据来源”。数据来源需要传入一个已经按序列排好的内表主程序通过OPEN_FORM、WRITE_FORM调用表单时把数据塞到全局内表里就行。指定内表后一定要做一次字段映射。在Loop内部把模板行里每个字段的“数据引用”关联到内表字段。这一步容易混淆的是不直接写ITAB-FIELD这种写法而是在字段属性里选择内表对应的表字段Smartforms自动生成绑定关系。如果后续内表结构调整了需要回头重新绑一下别偷懒不检查。3.2 用命令节点控制循环内部逻辑Loop内部可以有“命令”子节点这就相当于在一个循环体的内部做编程。可以在命令里写IF判断比如某个字段为空时显示特定文本也可以根据前一行的数据动态决定当前行是否加粗。由于这个命令节点是嵌在Loop内部的每一行数据都会触发一次命令逻辑所以性能开销要考虑在内。我在高性能场景下的习惯是尽量避免在命令节点里做重计算或数据库查询能提前在ABAP主程序里算好的就提前算好Loop里只做显示逻辑判断。例如把内表预先按打印顺序排序好在Loop里只根据已算好的字段判断加粗或变色效率会高非常多。3.3 Loop里的动态排序和行号显示行号是动态表格里的高频需求。Smartforms里没有直接的行号变量通常做法是给内表增加一个序号字段在ABAP主程序里用一个计数器循环填充。LOOP AT ITAB. lv_index sy-tabix. ITAB-seq_no lv_index. MODIFY ITAB. ENDLOOP.这样导进去的每一行就自带序号了。还有一种更骚的做法是利用模板行里放一个文本节点在“文本”的格式里设置日期/时间/计数器的自动格式化功能。但这个功能在不同版本里表现不稳定不如直接给内表加序号字段靠谱。我建议核心业务单据都采用“主程序加序号字段”方案可控性最强调试也最简单。3.4 动态显示与隐藏行的处理有时候表格需要根据数据字段的值决定某些行是否显示。比如明细为零的行不输出。这个需求放在TemplateLoop里有两种做法第一种是完全在ABAP主程序中过滤内表只传给Smartforms实际要打印的行这是最推荐的做法简单、性能好、不容易出错第二种是在Loop内部命令里设置一个变量控制模板行的“显示”属性这种做法灵活但变量作用域和条件逻辑容易写乱。我的原则很明确凡是能在外层ABAP解决的问题尽量不放进Smartforms的表单逻辑里。这是因为Smartforms的命令节点调试不方便错误信息不直观而ABAP主程序是完全可控、可单步调试的。保持表单逻辑“只会做展示”这样一个纯洁性后面维护会轻松很多。4. 实操过程从零搭一个多栏明细表4.1 需求描述和整体设计假设要做一张送货单表头是客户信息中间是物料明细多行明细每行有行号、物料编码、物料描述、数量、单位、单价、金额表尾是合计金额。需求里有几个难点明细行数不固定可能5行也可能30行每页都要带表头最后一页要额外输出签收区域金额要带千分位和货币符号。按前面的逻辑拆解表头和表尾用静态Template明细区画一行行模板外包一层Loop表头重复输出交给分页设置签收区域在最后一个Main Window里做条件判断。整体方案清晰后就可以动手在Smartforms里搭建了。4.2 创建页面和模板的详细步骤第一步在SMARTFORMS里新建格式类型ZSF_DELIVERY_NOTE进入页面节点新建页面PAGE_MAIN。在页面上添加一个Main Window命名为MAIN_CONTENT高度设为页面可用高度的80%左右。第二步在这个Main Window里画模板。先把第一行拆成7列分别放行号、物料编码、物料描述、数量、单位、单价、金额。调整列宽行号窄列、物料编码中等列、物料描述宽列、数量和单位中等偏窄列、单价和金额中等列。然后再画表头行在模板上方用文本写列名且把表头行的样式设置为每页重复显示在属性里不勾选“只第一页输出”。第三步在明细行模板外部包一个Loop。Loop数据源指定内表GT_ITEM。把模板里7个单元格的数据引用分别绑定到GT_ITEM的对应字段上。注意物料描述字段的“文本模式”要设置为多行自动换行这样超长描述不会把单元格撑爆。4.3 驱动程序的编写与细节调优主程序里需要先造数据内表再依次调用表单功能模块。核心代码块大致如下DATA: ls_item LIKE LINE OF gt_item. DATA: lv_tabix TYPE i. * 给内表添加序号 LOOP AT gt_item INTO ls_item. lv_tabix sy-tabix. ls_item-seqno lv_tabix. MODIFY gt_item FROM ls_item INDEX lv_tabix. ENDLOOP. CALL FUNCTION OPEN_FORM EXPORTING form ZSF_DELIVERY_NOTE EXCEPTIONS OTHERS 1. IF sy-subrc 0. CALL FUNCTION WRITE_FORM EXPORTING element MAIN EXCEPTIONS OTHERS 1. ENDIF. CALL FUNCTION CLOSE_FORM EXCEPTIONS OTHERS 1.WRITE_FORM里的element要与Smartforms里Main Window节点的名称保持一致。这里容易犯的错是大小写不敏感但在名称上拼写不同导致打印出来一片空白。4.4 调试输出与效果验证表单调试有两个层面一是语法层面在SMARTFORMS里直接点“激活”如果有语法错误会直接报在节点上二是运行层面主程序执行后看Spool输出。建议第一次调试时直接用/nsmartforms进入表单点“测试”按钮输入驱动数据的程序名或直接预览这一步可以快速看到模板布局是否变形。我把一个常用的调试技巧放在这里如果在Spool里发现输出内容与预览完全一致但实际打印出来位置偏移优先检查打印机驱动和纸张类型设置如果只在网格线显示上出现问题则是模板边框属性与Main Window尺寸之间互相冲突。这两类问题都被我误判过多次后来总结出规律就快了。5. 常见问题与避坑指南5.1 在LOOP里下动态断点怎么玩这个标题下的热搜词里正好有“在abap中loop里面debug怎么设置动态断点”据说是很多ABAP开发查过的痛点。最笨的办法是在ABAP主程序里对GT_ITEM循环时按下断点单步推进到目标行。但Smartforms表单内部的Loop并不会直接与主程序的循环同步所以主程序的断点看不到表单里循环的状态。想在Smartforms内部的Loop里调试任意一行常用的方式是在Loop节点的“命令”子节点里插入一段临时代码IF sy-index 100. BREAK-POINT. ENDIF.sy-index在Smartforms的Loop命令中代表当前第几次循环。你只需要在目标行的序号处设置断点激活后运行程序就会在循环体内部击中调试器。击中的位置是表单函数模块内部代码可以查看当前传入的实参、内表当前行的值以及表单变量。这种方法比在外层程序里猜字段值要直观得多。断点调试完这段临时代码必须移除或注释掉否则生产系统上每次跑到第100行就会停一次。我见过有同事忘记删断点半夜跑批打到一半挂掉的印象极其深刻。5.2 竖线错位和行高失控的经典BugTemplate方式做表格最经典的问题是打印出来后竖线不在一条直线上。原因几乎都是每一行的边框线设置不一致导致的。Smartforms是否显示一条竖线取决于该行是否设置了左边框。如果某些行设置了左边框某些行没设竖直方向上就会多出一条竖线或断掉一条竖线。解决办法是在设计模板时统一横竖线规则。我的金标准是每一列靠左边框显示竖线所有行都保持这一规则最右侧的竖线由范围边框或最后一条模板行的右边框负责。这样每行复制后竖线自动连贯成列。如果某行为空不需要显示竖线那要整体关闭该行的所有分割线而不是只关竖线。行高失控常见于文字自动换行后行高被撑开。Template的单元格如果设置允许自动增高那么即使给了固定行高实际渲染时也会因内容变高而撑高。这种撑高在单行行模板里会出现“某行特别高其他行正常”的锯齿形分布。想根治要么在单元格属性里禁止自动增高要么预估最大字符数在ABAP端就截断文本。对于金额、数量这类核心字段我绝对禁止自动增高描述类字段允许自动增高但必须用gt_item里单独格式化好的文本字段来控制长度。5.3 分页后表头不重复出现怎么办Loop数据量大了以后内容会自动分页。用户通常要求第二页、第三页也要有表头。Smartforms的实现方式听起来简单在模板里把表头行设为“每页重复”但实际操作时很多人找不到这个开关或者设置了没生效。这个开关在模板行的“格式化”属性里有一个“输出时”的选项可以选择“仅第一页”或“每页”。关键坑点是如果要重复的那几行不是独立模板行而是被合并进数据行模板里的那么这个开关就失效了。正确做法是表头行一定要单独画成一个独立模板区域放在Loop上面而不是包含在Loop内部。这样系统才能识别它作为“每页顶部”的重复元素。如果重复设置了仍然失效还有一个替代方案新建多个页面类型比如PAGE_MAIN和PAGE_NEXT在PAGE_NEXT里也放相同的表头模板Loop的分页目标指向PAGE_NEXT。这种方法比属性开关更可控适合对页面结构有精细要求的场景。5.4 中文乱码和字体不显示的排查方法Smartforms打印中文如果出现乱码、方块或完全不显示通常不是数据问题而是字体设置问题。Smartforms的文本节点里字体可以单独指定。很多默认模板用的是供应商内置非中文字体所以中文直接穿透过去。解决方案是在涉及中文的字段单元格里把字体选择为HEITI黑体或SONG宋体这类中文字体并且在字体属性里把语言设置为中文。有同事问为什么设置完还是不行多半是只在单元格级别设置没有在模板行级别设置或者因为模板行里有多个子字段只改了其中一个。更隐蔽的一种情况是Smartforms预览正常但用PDF输出后中文丢失。这种情况通常与打印后台的PDF转换配置有关需要在/nspad里检查设备类型是否支持中文字体嵌入。如果你的项目走的是SAP标准打印队列这一步要跟BASIS一起核对光改表单本身救不回来。5.5 大循环性能问题的检查点Loop处理几千行数据时性能会变得很难看。常见瓶颈有两个一是Loop命令节点里做了数据库查询二是在Smartforms里频繁操作内表导致表单引擎重建上下文。排查性能问题时先打开ST05或者SAT跟踪事务看整体运行时间消耗在哪里。如果集中在表单相关的函数模块就重点检查Loop内部的命令代码。我的优化原则上面提过就是能预计算的全在ABAP程序里预计算表单内只做字段引用和简单文本拼接。另外一个容易被忽略的点是Smartforms的Loop在每次迭代时都会重新计算模板里的所有表达式。如果模板里的某个字段绑定的是一个复杂的格式化表达式比如带条件判断的嵌套字符串那把这个表达式提前算好放在内表字段里比让Smartforms逐个解释要快得多。6. 我实际跑项目时不断调整出来的一些心得最后聊点不常写进文档里的体会。TemplateLoop这个组合做到后面你会发现最关键的其实不是Smartforms本身而是你怎样把业务方的表格需求进行结构化拆解。业务人员画出来的表格草稿通常都是一整张不会告诉你哪些部分是固定模板、哪些部分会动态增长。你在和业务沟通时多问一句“这一列最多可能有多少数据”和“跨页时这张表头要不要重复”能把后期返工量降低一半。还有一点是版本兼容性。不同SAP版本对Smartforms的编辑器支持和格式解释有细微差异。比如ECC 6.0和S/4 HANA里对垂直边框的渲染就有不同行为。我现在的项目里很多表单是从ECC升级来的升级后必须逐一检查Template的边框和Main Window高度否则打印出来就是歪的。这块没有捷径只能靠回归测试清单一条一条过。关于团队协作也有个建议Smartforms不像ABAP代码那么容易做版本对比。所以每改一次模板我都截一张预览图存到版本目录里标明改动时间、改动点和改动前后的差异。这个习惯救过我很多次有时候是业务方说“以前不是这样”你把截图甩出来比解释一百句都有用。我在实际写Smartforms时最顺手的配置是驱动数据在ABAP端一次整理干净表单内部只管位置和排版。如果你现在用TemplateLoop做复杂表格觉得别扭十有八九是你把太多业务判断塞进了表单里。试着把判断逻辑往外挪你会发现整个表单的稳定性立刻上一个台阶。最后再分享一个小技巧模板里所有金额和日期字段不要直接在Smartforms里格式化提前在ABAP内表里用WRITE TO加EDIT_MASK完成格式化然后作为字符字段传进去。这样做第一是性能好第二是Smartforms自带的格式定义在某些代码页下会丢失前导零或千分位ABAP端格式化后这个风险就完全可控了。这套流程我用了多个项目还没有遇到过没法收场的复杂表格。
返回列表