SAP BSP 报表开发实战:从 SE80 建应用到事件处理全流程
简介这份资源是面向SAP ABAP开发者的BSP报表开发实例适合已掌握ABAP基础、希望入门SAP Web应用与动态报表的初中级开发者。内容围绕SE80中创建BSP应用、页面与CSS引用重点演示ABAP脚本与HTML混合编写包括表单提交、事件处理及内部表循环绑定表格数据帮助读者理解BSP如何将数据库数据与前端页面结合解决传统报表交互性不足的问题。资源包为1个doc文档大小约433KB以图文步骤与代码片段呈现完整开发流程便于对照练习。目前已有378人学习下载可作为BSP报表开发的入门参考快速掌握页面布局、样式引用与ABAP逻辑交互的核心思路。1. 从一张订单报表说起BSP 到底能解决什么手里有一套 SAP 系统业务方要一张按客户编号和订单日期查询的销售订单报表字段横跨 VBAK、VBAP、KNA1、TVGRT、PA0001 好几张表还要在网页上直接输入条件、点按钮出结果。这种需求放在今天很多人第一反应是丢给 Fiori 或者 OData 服务但在一些老系统里BSP 仍然是成本最低、落地最快的方案。BSP 全称 Business Server Pages是 SAP 早期提供的 Web 开发技术页面里用% %嵌 ABAP 代码语法结构跟 JSP 几乎一样区别只是尖括号里跑的是 ABAP 而不是 Java。这份 BSP 报表开发实例HTML就是一份典型的“从零搭一张可交互报表”的完整记录覆盖了 SE80 建应用、建页面、引 CSS、写 Layout、写 Event Handler、定义 Page Attribute 和 Type Definition 的全流程。适合谁看一是维护老 SAP 系统、需要快速交付 Web 报表的 ABAP 开发者二是想理解 BSP 页面生命周期和事件模型、为后续迁移或重构做技术储备的工程师。下面我按实际动手顺序把这份实例拆开讲透。2. SE80 建应用与页面从空壳到可运行骨架2.1 创建 BSP Application 的入口与命名约束事务代码 SE80 进去之后左侧对象导航里选 BSP Application输入程序名回车系统会提示不存在并给出创建选项。这里有个容易忽略的点BSP 应用名在同一个系统里必须唯一而且一旦激活后改名成本很高因为后续页面引用、导航跳转都会绑死这个名字。常见做法是先用Z或Y开头后面跟模块缩写比如ZREPORT_ORDER这种风格既符合命名规范也方便在 SE80 里按前缀过滤。创建时系统会让你选描述和包Package如果是临时验证可以先放$TMP但正式交付一定要挂到传输请求对应的包里否则后面跨系统传输会漏掉。这一步没有代码但选错包是后期“报表在开发机好好的、到测试机打不开”的经典原因之一。2.2 页面类型选择与 Layout 页签的职责在 BSP 应用下新建页面需要填网页名字、描述、页面类型。页面类型一般选 HTML因为这份实例就是纯 HTML 报表不需要走其他渲染模式。建完之后你会看到几个页签Layout、Event Handler、Page Attributes、Type Definition 等。Layout 页签负责页面静态结构和动态输出的混合也就是 HTML 骨架加% %表达式Event Handler 负责处理用户交互事件比如按钮点击Page Attributes 定义页面级变量Type Definition 定义内表类型和结构。这个分工要理解清楚否则很容易把取数逻辑写到 Layout 里导致页面渲染和业务逻辑搅在一起后面改一个字段要翻半天。我一般的原则是Layout 只做展示和循环输出所有 SELECT、校验、导航都放 Event Handler。2.3 引用 CSS 文件的正确姿势实例里用link relstylesheet href./style.css typetext/css引入样式。这里的关键是路径。BSP 应用里的静态文件CSS、JS、图片通常放在 MIME 对象下./style.css是相对当前页面路径去找。如果你把 CSS 放在 BSP 应用的 MIME 目录里这个写法能生效如果放在别的路径就要改成对应的相对路径或绝对路径。另一个坑是BSP 页面在 Layout 里写link标签时位置要放在HEAD里不要放到form内部。实例代码里link出现在html之后、HEAD之前浏览器大多能容错解析但严格来说不规范迁移到某些前端框架或做 HTML 校验时会报问题。建议统一写成head link relstylesheet href./style.css typetext/css title订单报表/title /head这样结构清晰也方便后续加多个样式文件。3. Layout 与 Event Handler 联动把输入框、按钮和取数逻辑串起来3.1 Layout 里的表单结构与 ABAP 表达式Layout 页签的核心是把 HTML 表单和 ABAP 变量绑定。实例里有两个输入框分别对应客户编号st_custno和订单日期st_ord值通过%gt_custno%和%gt_dat%回填。提交按钮的 name 是onInputProcessing(sub)这个命名很关键——它告诉 BSP 运行时点击按钮后去 Event Handler 里找when sub分支。下面这段是简化后的 Layout 结构保留了核心绑定逻辑%page languageabap % h3% errormsg %/h3 html link relstylesheet href./style.css typetext/css headtitle订单报表/title/head form namePageForm idPageForm methodpost table aligncenter border0 width80% tr td客户编号 input classInputBox typetext namest_custno size10 value%gt_custno% maxlength10 /td /tr tr td订单日期 input classInputBox typetext namest_ord size10 value%gt_dat% maxlength10 /td /tr tr td colspan2 alignright input nameonInputProcessing(sub) classform1 typesubmit value查询 /td /tr /table !-- 下方是数据表格循环输出内表 -- table border0 width100% classtable1 cellspacing1 tr classth td凭证日期/tdtd销售凭证号/tdtd采购订单编号/td td员工姓名/tdtd销售组描述/tdtd交货日期/td td累计订购量/tdtd订单金额/tdtd已出货金额/tdtd产品组描述/td /tr % DATA: wa_YSHDDCX LIKE LINE OF ZSDFB_YSHDDCX. LOOP AT ZSDFB_YSHDDCX INTO wa_YSHDDCX. % tr td nowrap aligncenter%wa_YSHDDCX-AUDAT%/td td nowrap aligncenter%wa_YSHDDCX-VBELN%/td td nowrap aligncenter%wa_YSHDDCX-BSTNK%/td td nowrap aligncenter%wa_YSHDDCX-SMNAM%/td td nowrap aligncenter%wa_YSHDDCX-BEZEI20%/td td nowrap aligncenter%wa_YSHDDCX-VDATU%/td td nowrap alignright%wa_YSHDDCX-ZJSL%/td td nowrap alignright%wa_YSHDDCX-KZWI2%/td td nowrap alignright%wa_YSHDDCX-YCHJE%/td td nowrap aligncenter%wa_YSHDDCX-VTEXT%/td /tr % ENDLOOP. % /table /form /html逻辑说明%page languageabap %声明页面脚本语言% %是输出表达式把 ABAP 变量值写到 HTML 流里% %是代码块里面可以写 DATA、LOOP、SELECT 等语句。参数方面namest_custno和namest_ord是表单字段名Event Handler 里通过navigation-get_parameter按这个名字取值。maxlength10限制输入长度和后面 ABAP 变量gt_custno TYPE VBAK-KUNNR的长度对齐避免截断问题。3.2 Event Handler 里的取值、校验与导航Event Handler 是 BSP 页面的事件入口。实例里case event_id. when sub.对应按钮点击。核心步骤是先navigation-set_parameter把表单字段注册进来再navigation-get_parameter取值赋给页面属性然后做非空校验最后执行 SELECT 取数并填充内表。* Event handler for checking and processing user input case event_id. when sub. * 取得输入值 navigation-set_parameter( st_custno ). gt_custno navigation-get_parameter( name st_custno ). navigation-set_parameter( st_ord ). gt_dat navigation-get_parameter( name st_ord ). * 输入判断 clear errormsg. if gt_custno is not initial. unpack gt_custno to gt_custno. else. errormsg 请输入客户编号. exit. endif. * 取值区 DATA: wa_YSHDDCX LIKE LINE OF ZSDFB_YSHDDCX. select KUNNR VBELN BSTNK AUDAT VDATU SPART VKGRP VTWEG into corresponding fields of table ZSDFB_YSHDDCX from VBAK where KUNNR eq gt_custno and AUDAT eq gt_dat. loop at ZSDFB_YSHDDCX into wa_YSHDDCX. * 取得客户描述 select single NAME1 from KNA1 into wa_YSHDDCX-NAME1 where KUNNR wa_YSHDDCX-KUNNR. * 取销售组代码对应的描述 select single BEZEI from TVGRT into wa_YSHDDCX-BEZEI20 where VKGRP wa_YSHDDCX-VKGRP. * 取销售雇员代码 select single T~PERNR from VBPA as T into wa_YSHDDCX-BUPARTNR where T~PARVW VE and T~VBELN wa_YSHDDCX-VBELN. * 取销售雇员名称 select single PA0001~SNAME from PA0001 into wa_YSHDDCX-SMNAM where PA0001~PERNR wa_YSHDDCX-BUPARTNR. * 取订单金额 select sum( KZWI2 ) from VBAP into wa_YSHDDCX-KZWI2 where VBELN wa_YSHDDCX-VBELN. * 取得产品组描述 select single VTEXT from TSPAT into corresponding fields of wa_YSHDDCX where SPART wa_YSHDDCX-SPART. * 取得分销渠道描述 select single VTEXT from TVTWT into wa_YSHDDCX-VTEXT where VTWEG wa_YSHDDCX-VTWEG and SPRAS 1. modify ZSDFB_YSHDDCX from wa_YSHDDCX. endloop. when others. endcase.逻辑说明navigation-set_parameter把表单字段名注册到导航上下文get_parameter按名字取值。unpack用于去掉前导空格因为网页输入框传过来的值可能带空格。select ... into corresponding fields of table一次性把 VBAK 的字段填到内表字段名要跟内表结构里的字段名一致。循环里对每一行做多次select single补全描述字段最后modify更新内表。参数方面SPRAS 1是语言键代表中文如果系统语言是 EN 就要改成E否则描述取不到。3.3 Page Attribute 与 Type Definition 的配合Page Attributes 里定义页面级变量errormsg TYPE STRING、gt_custno TYPE VBAK-KUNNR、gt_dat TYPE D、ZSDFB_YSHDDCX TYPE TT_YSHDDCX。Type Definition 里定义TYPES: tt_YSHDDCX TYPE STANDARD TABLE OF ZSDFB_YSHDDCX INITIAL SIZE 0.。这里ZSDFB_YSHDDCX是自定义结构字段包括 KUNNR、NAME1、VBELN、BSTNK、AUDAT、KZWI2、VDATU、SPART、ZJSL、YCHJE、VTEXT、VKGRP、BEZEI20、BUPARTNR、SMNAM、VTWEG 等。关键点是Page Attribute 里声明的内表类型必须和 Type Definition 里定义的类型名一致否则激活会报“类型未定义”。另外INITIAL SIZE 0表示初始不分配内存对报表这种数据量不大的场景够用如果数据量预期很大可以改成INITIAL SIZE 100减少扩容开销。4. 避坑与排查BSP 报表开发里最容易翻车的五个点4.1 页面激活报错“类型 TT_YSHDDCX 未定义”现象Layout 或 Event Handler 里用了TT_YSHDDCX激活时提示类型不存在。原因Type Definition 页签里没有定义这个类型或者定义在了错误的页面里。解决确认 Type Definition 里写了TYPES: tt_YSHDDCX TYPE STANDARD TABLE OF ZSDFB_YSHDDCX INITIAL SIZE 0.并且ZSDFB_YSHDDCX这个结构已经在数据字典里创建并激活。如果结构是新建的记得先激活结构再激活 BSP 页面。4.2 按钮点击后页面刷新但数据不显示现象输入客户编号点查询页面重新加载了但表格是空的也没有错误提示。原因Event Handler 里when sub没有匹配上或者navigation-get_parameter取到的值是空的。解决先检查按钮 name 是否严格写成onInputProcessing(sub)括号和大小写都要一致再在 Event Handler 里加errormsg gt_custno.临时输出确认取值是否成功。如果取值为空检查set_parameter是否在get_parameter之前调用。4.3 SELECT 取数结果和内表字段对不上现象内表里有数据但某些字段显示为空或乱码。原因select ... into corresponding fields of table要求数据库字段名和内表字段名完全一致如果内表里字段名是BEZEI20而数据库里是BEZEI就对不上。解决要么在 SELECT 里用别名要么在内表结构里把字段名改成和数据库一致。实例里BEZEI20是自定义字段名所以用了单独的select single ... into wa_YSHDDCX-BEZEI20来赋值没有走corresponding fields。4.4 中文描述取不到显示为空白现象客户名称、销售组描述这些中文字段在报表里是空的。原因SPRAS参数设成了1但系统语言键可能不是1或者文本表里没有对应语言的条目。解决先用 SE16 查 TVTWT 表确认SPRAS字段的实际值中文通常是1英文是E。如果查不到说明该描述在文本表里没有维护需要先补数据。4.5 内表循环里 MODIFY 不生效现象循环里对wa_YSHDDCX赋值后modify但 Layout 里输出的还是旧值。原因modify ZSDFB_YSHDDCX from wa_YSHDDCX.没有带索引或条件默认按表头内表的主键更新如果内表没有定义主键可能更新不到正确行。解决在loop at ... into wa_YSHDDCX之后用modify ZSDFB_YSHDDCX from wa_YSHDDCX.是可行的因为into会带出当前行索引但如果用的是into corresponding fields或assigning行为会不同。稳妥做法是用loop at ... into wa_YSHDDCX. ... modify ZSDFB_YSHDDCX from wa_YSHDDCX. endloop.这种标准写法不要混用field-symbols。5. 进阶技巧用导航参数和页面跳转做多页报表5.1 用 navigation-next_page 做页面间传参实例里注释掉了navigation-next_page( 要关联的值 )和navigation-goto_page(custord_list.htm)。这两个是 BSP 页面跳转的两种方式next_page按属性里定义的目标页面跳转goto_page直接指定页面文件名。如果要做“查询结果页”和“明细页”分离可以在查询页的 Event Handler 里把客户编号和日期通过set_parameter传到导航上下文然后在明细页的 Layout 里用navigation-get_parameter取出来。* 在查询页 Event Handler 里 navigation-set_parameter( name st_custno value gt_custno ). navigation-set_parameter( name st_ord value gt_dat ). navigation-next_page( ORDER_DETAIL ). * 在明细页 Event Handler 里 DATA: lv_custno TYPE VBAK-KUNNR. navigation-set_parameter( st_custno ). lv_custno navigation-get_parameter( name st_custno ).参数说明set_parameter的name是参数名value是参数值get_parameter按name取值。注意set_parameter在跳转前调用get_parameter在目标页面的 Event Handler 里调用。如果目标页面是静态 HTML 没有 Event Handler可以在 Layout 里用navigation-get_parameter直接输出。5.2 用 CSS 类控制表格样式和输入框对齐实例里用了classInputBox、classform1、classtable1、classth这些样式类。CSS 文件里可以这样定义.InputBox { border: 1px solid #999; height: 20px; line-height: 20px; padding: 0 4px; } .form1 { background-color: #e0e0e0; border: 1px solid #666; height: 24px; padding: 0 12px; cursor: pointer; } .table1 { border-collapse: collapse; font-size: 12px; } .table1 .th { background-color: #d9e5f2; font-weight: bold; } .table1 td { border: 1px solid #ccc; padding: 2px 6px; }这样表格有表头底色、单元格边框输入框和按钮也有统一风格。注意border-collapse: collapse要加在 table 上否则cellspacing1和 CSS 边框会叠加出双线效果。5.3 验证方法从 SE80 测试到浏览器调试保存激活后在 SE80 里右键 BSP 应用选“测试”系统会生成一个 URL 在浏览器打开。如果页面报 500 错误先看 SAP 系统里的 ST22 短转储里面会给出 ABAP 运行时错误的行号和原因。如果页面能打开但样式不对按 F12 打开浏览器开发者工具看 Network 里style.css是否 404。如果 404说明 CSS 文件没有放到 MIME 目录或者路径写错了。我一般会按这个顺序排查先确认 BSP 应用和页面都激活了再确认 MIME 对象存在且路径正确然后看 Event Handler 里的when分支是否命中最后查 SELECT 语句在 SE16 里能否查到数据。这套流程走下来大部分问题都能定位到具体行。从那以后我每次建 BSP 页面都强制先把 Page Attribute 和 Type Definition 定义完再写 Layout 和 Event Handler避免激活时来回跳页签找错误。希望帮到你。本文还有配套的精品资源点击获取