表開發(fā)實(shí)戰(zhàn):從 SE80 到可交互 HTML 報(bào)表完整落地)
簡(jiǎn)介這份資源是面向SAP ABAP開發(fā)者的BSP報(bào)表開發(fā)實(shí)例適合已掌握ABAP基礎(chǔ)、希望入門SAP Web應(yīng)用與動(dòng)態(tài)報(bào)表的初中級(jí)開發(fā)者。內(nèi)容圍繞SE80中創(chuàng)建BSP應(yīng)用與頁面、引用CSS樣式、在HTML中嵌入ABAP腳本、通過表單與事件處理實(shí)現(xiàn)用戶交互以及用loop循環(huán)將內(nèi)部表數(shù)據(jù)綁定到HTML表格等核心環(huán)節(jié)展開幫助讀者理解BSP如何把數(shù)據(jù)庫(kù)數(shù)據(jù)與前端頁面結(jié)合構(gòu)建可交互的Web報(bào)表。資源包為1個(gè)doc文檔約433KB以圖文步驟與代碼片段為主便于對(duì)照練習(xí)。目前已有378人學(xué)習(xí)下載。通過該實(shí)例讀者可掌握BSP應(yīng)用創(chuàng)建流程、頁面布局與樣式引用、ABAP表達(dá)式動(dòng)態(tài)輸出、表單提交與onInputProcessing事件處理等關(guān)鍵技能并積累將ABAP邏輯與HTML界面整合的實(shí)戰(zhàn)經(jīng)驗(yàn)為后續(xù)開發(fā)更復(fù)雜的SAP Web報(bào)表打下基礎(chǔ)。1. BSP 報(bào)表開發(fā)實(shí)例從 SE80 到可交互 HTML 報(bào)表的完整落地手里有一份 2007 年的 BSP 報(bào)表開發(fā)實(shí)例作者把 SE80 建應(yīng)用、頁面創(chuàng)建、CSS 引用、Layout 與 Event Handler 代碼、Page Attribute 變量定義、Type Definition 類型定義、測(cè)試效果和注意事項(xiàng)全寫進(jìn)去了。這份東西放到今天看技術(shù)棧確實(shí)老但它的價(jià)值不在“新”而在“完整”——它是一條從零到可運(yùn)行 BSP 報(bào)表的最短路徑?jīng)]有省略任何一步。如果你正在維護(hù)一套跑在 SAP 里的 BSP 應(yīng)用或者需要快速給業(yè)務(wù)部門出一個(gè)能輸入客戶編號(hào)和訂單日期、點(diǎn)提交就出表格的查詢頁面這份實(shí)例能直接抄作業(yè)。它適合兩類人一是剛接觸 ABAP Web 開發(fā)、需要理解 BSP 頁面生命周期和事件處理機(jī)制的新手二是手頭有老 BSP 系統(tǒng)、需要快速改一個(gè)報(bào)表或排查問題的熟手。下面我按“資源是什么、怎么用、坑在哪”的順序把這份實(shí)例拆開講透。2. BSP 應(yīng)用創(chuàng)建與頁面結(jié)構(gòu)SE80 里的五個(gè)關(guān)鍵動(dòng)作2.1 為什么選 BSP 而不是 Web Dynpro這份實(shí)例用的是 BSP不是 Web Dynpro。BSP 的核心思路是“HTML 里嵌 ABAP”頁面結(jié)構(gòu)接近傳統(tǒng) JSPLayout 負(fù)責(zé)展示、Event Handler 負(fù)責(zé)邏輯、Page Attribute 負(fù)責(zé)變量、Type Definition 負(fù)責(zé)類型。對(duì)于報(bào)表類需求——輸入條件、查數(shù)據(jù)庫(kù)、循環(huán)輸出表格——BSP 的代碼量比 Web Dynpro 少得多頁面結(jié)構(gòu)也更直觀。Web Dynpro 適合復(fù)雜交互和組件復(fù)用但如果你只是要一個(gè)“輸入框加提交按鈕加結(jié)果表格”的查詢頁BSP 的 Layout 里直接寫 HTML 表格、用% %綁定變量開發(fā)速度更快。這份實(shí)例選 BSP 是合理的因?yàn)樗哪繕?biāo)就是一個(gè)簡(jiǎn)單報(bào)表不需要復(fù)雜的頁面導(dǎo)航和組件模型。2.2 SE80 創(chuàng)建 BSP 應(yīng)用的完整步驟打開事務(wù)代碼 SE80在左側(cè)對(duì)象導(dǎo)航里選擇 BSP Application輸入你規(guī)劃好的 BSP 程序名回車后系統(tǒng)會(huì)提示是否創(chuàng)建確認(rèn)即可。創(chuàng)建完成后在 BSP 應(yīng)用節(jié)點(diǎn)下右鍵新建 Page輸入網(wǎng)頁名稱和描述頁面類型選 HTML確認(rèn)保存。這一步的關(guān)鍵是頁面名稱要和后續(xù) Event Handler 里goto_page或next_page引用的名稱一致否則運(yùn)行時(shí)會(huì)報(bào)頁面找不到。常見做法是頁面名用xxx.htm格式比如custord_list.htm這樣在代碼里引用時(shí)不容易寫錯(cuò)。創(chuàng)建頁面后你會(huì)看到 Layout、Event Handler、Page Attribute、Type Definition 等頁簽。Layout 是 HTML 和 ABAP 腳本混合的地方Event Handler 是純 ABAP 邏輯Page Attribute 定義頁面級(jí)變量Type Definition 定義類型。這四個(gè)頁簽的代碼是相互配合的缺一個(gè)頁面就跑不起來。2.3 Layout 頁簽的代碼結(jié)構(gòu)與參數(shù)說明Layout 頁簽的代碼是這份實(shí)例的核心。開頭%page languageabap %聲明頁面使用 ABAP 腳本語言這是 BSP 的固定寫法。接著h3 % errormsg % /h3把 Page Attribute 里定義的errormsg變量輸出到頁面用于顯示錯(cuò)誤提示。link relstylesheet href./style.css typetext/css引用 CSS 文件路徑是相對(duì)路徑CSS 文件需要放在 BSP 應(yīng)用的 MIME 資源里或者通過其他方式關(guān)聯(lián)。表單部分用form namePageForm idPageForm methodpost定義里面有兩個(gè)輸入框st_custno綁定gt_custnost_ord綁定gt_dat。注意value%gt_custno%這種寫法它把 ABAP 變量的值輸出到 HTML 的 value 屬性里頁面刷新后輸入框會(huì)保留上次輸入的值。提交按鈕的name屬性是onInputProcessing(sub)這個(gè)寫法是 BSP 的事件綁定語法意思是點(diǎn)擊按鈕時(shí)觸發(fā) Event Handler 里case event_id. when sub.分支。表格部分用%DATA: wa_YSHDDCX LIKE LINE OF ZSDFB_YSHDDCX.loop at ZSDFB_YSHDDCX into wa_YSHDDCX.%開始循環(huán)然后在tr里用%wa_YSHDDCX-AUDAT%這樣的表達(dá)式逐列輸出數(shù)據(jù)。循環(huán)結(jié)束用%endloop.%。這里要注意ZSDFB_YSHDDCX是一個(gè)內(nèi)表在 Page Attribute 里定義為TYPE TT_YSHDDCX而TT_YSHDDCX在 Type Definition 里定義為STANDARD TABLE OF ZSDFB_YSHDDCX。這個(gè)鏈條不能斷Type Definition 定義類型Page Attribute 定義變量Layout 里循環(huán)輸出。2.4 Event Handler 與 Page Attribute 的配合邏輯Event Handler 里的代碼負(fù)責(zé)處理用戶輸入和查詢數(shù)據(jù)。case event_id. when sub.匹配提交按鈕的事件。首先用navigation-set_parameter(st_custno).和navigation-get_parameter( name st_custno ).取得輸入值存到gt_custno。然后做輸入校驗(yàn)如果gt_custno為空設(shè)置errormsg 請(qǐng)輸入客戶編號(hào)并exit頁面會(huì)重新顯示并帶上錯(cuò)誤信息。校驗(yàn)通過后用select ... into corresponding fields of table ZSDFB_YSHDDCX from VBAK where KUNNR eq gt_custno and AUDAT eq gt_dat.查詢銷售訂單數(shù)據(jù)。接著loop at ZSDFB_YSHDDCX into wa_YSHDDCX.循環(huán)內(nèi)表在循環(huán)里用select single逐條取客戶名稱、銷售組描述、銷售雇員名稱、訂單金額、產(chǎn)品組描述、分銷渠道描述然后modify ZSDFB_YSHDDCX from wa_YSHDDCX.更新內(nèi)表。這個(gè)“循環(huán)內(nèi) select single 再 modify”的模式是 BSP 報(bào)表里常見的寫法但也是性能隱患后面避坑章節(jié)會(huì)細(xì)說。Page Attribute 里定義的變量errormsg TYPE STRING、gt_custno TYPE VBAK-KUNNR、gt_dat TYPE DZSDFB_YSHDDCX、ZSDFB_YSHDDCX TYPE TT_YSHDDCX。注意gt_dat的類型寫的是DZSDFB_YSHDDCX這可能是筆誤或者自定義類型實(shí)際應(yīng)該是日期類型。ZSDFB_YSHDDCX是內(nèi)表變量在 Layout 里直接循環(huán)輸出。Type Definition 里定義TYPES: tt_YSHDDCX TYPE STANDARD TABLE OF ZSDFB_YSHDDCX INITIAL SIZE 0.ZSDFB_YSHDDCX是自定義結(jié)構(gòu)字段包括 KUNNR、NAME1、VBELN、BSTNK、AUDAT、KZWI2、VDATU、SPART、ZJSL、YCHJE、VTEXT、VKGRP、BEZEI20、BUPARTNR、SMNAM、VTWEG、VTXTK 等。這些字段覆蓋了報(bào)表需要展示的所有列。3. 從輸入到輸出BSP 報(bào)表的完整數(shù)據(jù)流與代碼實(shí)現(xiàn)3.1 輸入校驗(yàn)與參數(shù)傳遞的代碼細(xì)節(jié)Event Handler 里的輸入校驗(yàn)邏輯是報(bào)表的第一道關(guān)卡。代碼先clear errormsg.清空錯(cuò)誤信息然后判斷if gt_custno is not initial.如果不為空?qǐng)?zhí)行unpack gt_custno to gt_custno.。unpack的作用是去掉輸入值的前導(dǎo)空格因?yàn)?HTML 輸入框提交的值可能帶空格。如果為空設(shè)置errormsg 請(qǐng)輸入客戶編號(hào)并exit頁面重新渲染時(shí)h3 % errormsg % /h3就會(huì)顯示錯(cuò)誤提示。訂單日期的校驗(yàn)在實(shí)例里被注釋掉了但邏輯是一樣的判斷gt_dat是否為空為空則設(shè)置錯(cuò)誤信息并退出。實(shí)際開發(fā)中建議把日期校驗(yàn)打開因?yàn)閟elect語句里AUDAT eq gt_dat如果gt_dat為空查詢條件會(huì)變成AUDAT eq 00000000查不到任何數(shù)據(jù)用戶會(huì)以為系統(tǒng)壞了。參數(shù)傳遞用navigation-set_parameter和navigation-get_parameter。set_parameter把輸入框的值綁定到 navigation 對(duì)象get_parameter從 navigation 對(duì)象取值賦給 ABAP 變量。這個(gè)機(jī)制是 BSP 頁面間傳參的標(biāo)準(zhǔn)做法next_page和goto_page也依賴 navigation 對(duì)象傳遞參數(shù)。3.2 數(shù)據(jù)庫(kù)查詢與內(nèi)表循環(huán)的寫法查詢主數(shù)據(jù)用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.。into corresponding fields of table表示按字段名匹配把 VBAK 的字段值填入內(nèi)表ZSDFB_YSHDDCX的同名字段。這種寫法要求內(nèi)表結(jié)構(gòu)里的字段名和數(shù)據(jù)庫(kù)表字段名一致否則對(duì)應(yīng)不上。循環(huán)內(nèi)表時(shí)每一條訂單記錄都要補(bǔ)充客戶名稱、銷售組描述、銷售雇員、訂單金額、產(chǎn)品組描述、分銷渠道描述。代碼用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.取銷售雇員編號(hào)再用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.匯總。產(chǎn)品組描述和分銷渠道描述分別從 TSPAT 和 TVTWT 取。每取完一條記錄的所有補(bǔ)充字段執(zhí)行modify ZSDFB_YSHDDCX from wa_YSHDDCX.把更新后的行寫回內(nèi)表。循環(huán)結(jié)束后內(nèi)表ZSDFB_YSHDDCX就包含了完整的報(bào)表數(shù)據(jù)Layout 里的loop at ZSDFB_YSHDDCX into wa_YSHDDCX.會(huì)逐行輸出。3.3 CSS 引用與頁面樣式控制CSS 引用在 Layout 的link relstylesheet href./style.css typetext/css這一行。./style.css表示當(dāng)前目錄下的 style.css 文件。在 BSP 應(yīng)用里CSS 文件通常放在 MIME 資源節(jié)點(diǎn)下或者通過 SE80 的 BSP 應(yīng)用資源管理上傳。引用后HTML 里的classInputBox、classform1、classTableDiv、classtable1、classth等樣式類就會(huì)生效。常見做法是把表格樣式、輸入框樣式、按鈕樣式都寫在 CSS 里L(fēng)ayout 里只寫結(jié)構(gòu)。這樣改樣式不用動(dòng) ABAP 代碼維護(hù)更方便。注意 CSS 文件路徑要寫對(duì)如果 BSP 應(yīng)用部署在子路徑下相對(duì)路徑可能失效可以用絕對(duì)路徑或者 BSP 提供的資源 URL 生成方式。3.4 測(cè)試與激活的檢查清單保存所有頁簽的代碼后點(diǎn)擊激活按鈕。激活時(shí)系統(tǒng)會(huì)檢查語法錯(cuò)誤如果有未定義的變量或類型會(huì)報(bào)錯(cuò)并阻止激活。激活成功后右鍵 BSP 應(yīng)用或頁面選擇測(cè)試系統(tǒng)會(huì)在瀏覽器里打開頁面。測(cè)試時(shí)重點(diǎn)檢查輸入框是否能輸入、提交按鈕是否觸發(fā) Event Handler、錯(cuò)誤信息是否顯示、表格是否有數(shù)據(jù)、CSS 樣式是否生效。如果頁面打開后空白先檢查 Layout 里是否有語法錯(cuò)誤導(dǎo)致頁面渲染中斷。如果提交后沒有反應(yīng)檢查 Event Handler 里case event_id. when sub.的sub是否和提交按鈕的name屬性一致。如果表格沒有數(shù)據(jù)檢查select語句的查詢條件是否匹配到了數(shù)據(jù)可以在 Event Handler 里加break-point調(diào)試。4. 避坑與排查BSP 報(bào)表開發(fā)中容易翻車的五個(gè)點(diǎn)4.1 內(nèi)表定義與循環(huán)處理的類型不匹配現(xiàn)象激活時(shí)報(bào)錯(cuò)“類型 TT_YSHDDCX 未定義”或“ZSDFB_YSHDDCX 不是內(nèi)表”。原因Type Definition 里定義的tt_YSHDDCX和 Page Attribute 里定義的ZSDFB_YSHDDCX類型不一致或者 Type Definition 沒有激活。解決確認(rèn) Type Definition 里TYPES: tt_YSHDDCX TYPE STANDARD TABLE OF ZSDFB_YSHDDCX INITIAL SIZE 0.已激活Page Attribute 里ZSDFB_YSHDDCX TYPE TT_YSHDDCX引用正確。循環(huán)時(shí)用DATA: wa_YSHDDCX LIKE LINE OF ZSDFB_YSHDDCX.定義工作區(qū)不要直接用內(nèi)表名當(dāng)工作區(qū)。4.2 循環(huán)內(nèi) SELECT SINGLE 導(dǎo)致的性能問題現(xiàn)象報(bào)表數(shù)據(jù)量稍大時(shí)頁面加載極慢甚至超時(shí)。原因loop at ZSDFB_YSHDDCX into wa_YSHDDCX.里每行都執(zhí)行多個(gè)select single如果內(nèi)表有幾百行數(shù)據(jù)庫(kù)查詢次數(shù)就是幾百乘以字段數(shù)。解決把能合并的查詢合并比如用select ... into table ... for all entries in ZSDFB_YSHDDCX一次性取客戶名稱、銷售組描述等然后在內(nèi)存里用read table匹配。如果數(shù)據(jù)量確實(shí)大考慮分頁或者限制查詢條件。4.3 輸入框值綁定與 UNPACK 的遺漏現(xiàn)象用戶輸入客戶編號(hào)后提交查詢不到數(shù)據(jù)但數(shù)據(jù)庫(kù)里明明有。原因HTML 輸入框提交的值可能帶前導(dǎo)空格gt_custno直接用于select的where條件時(shí)匹配不上。解決在 Event Handler 里對(duì)輸入值執(zhí)行unpack gt_custno to gt_custno.去掉前導(dǎo)空格。日期字段也要做類似處理或者用condense去掉多余空格。4.4 CSS 文件路徑錯(cuò)誤導(dǎo)致樣式失效現(xiàn)象頁面能打開數(shù)據(jù)也能顯示但表格沒有邊框、輸入框沒有樣式。原因link relstylesheet href./style.css typetext/css的路徑不對(duì)或者 CSS 文件沒有上傳到 BSP 應(yīng)用的 MIME 資源里。解決在 SE80 里檢查 BSP 應(yīng)用的 MIME 資源節(jié)點(diǎn)確認(rèn) style.css 存在。如果路徑不對(duì)改成正確的相對(duì)路徑或絕對(duì)路徑。測(cè)試時(shí)可以在瀏覽器里按 F12 看 Network 面板確認(rèn) style.css 是否 404。4.5 事件綁定名稱不一致導(dǎo)致提交無反應(yīng)現(xiàn)象點(diǎn)擊提交按鈕后頁面刷新但沒有任何查詢結(jié)果錯(cuò)誤信息也不顯示。原因提交按鈕的name屬性是onInputProcessing(sub)Event Handler 里case event_id. when sub.的sub必須和括號(hào)里的參數(shù)一致。如果寫成onInputProcessing(Submit)而 Event Handler 里是when sub.事件匹配不上。解決統(tǒng)一命名提交按鈕用onInputProcessing(sub)Event Handler 里用when sub.。改完后重新激活頁面再測(cè)試。5. 進(jìn)階技巧用 FOR ALL ENTRIES 優(yōu)化 BSP 報(bào)表查詢5.1 從逐行 SELECT 到批量查詢的改造思路原始實(shí)例的查詢邏輯是“先查主表到內(nèi)表再循環(huán)內(nèi)表逐行補(bǔ)充字段”。這種寫法在數(shù)據(jù)量小的時(shí)候沒問題但訂單行數(shù)一多數(shù)據(jù)庫(kù)交互次數(shù)線性增長(zhǎng)。優(yōu)化的核心思路是把“循環(huán)內(nèi) select single”改成“批量 select into table for all entries”。具體做法是主查詢不變把內(nèi)表ZSDFB_YSHDDCX作為驅(qū)動(dòng)表用for all entries in ZSDFB_YSHDDCX一次性取客戶名稱、銷售組描述、雇員信息等存到對(duì)應(yīng)的內(nèi)表里然后在循環(huán)里用read table匹配。5.2 FOR ALL ENTRIES 的代碼實(shí)現(xiàn)與參數(shù)說明 批量取客戶名稱 DATA: lt_kna1 TYPE STANDARD TABLE OF kna1. SELECT kunnr name1 INTO TABLE lt_kna1 FROM kna1 FOR ALL ENTRIES IN ZSDFB_YSHDDCX WHERE kunnr ZSDFB_YSHDDCX-kunnr. 批量取銷售組描述 DATA: lt_tvgrt TYPE STANDARD TABLE OF tvgrt. SELECT vkgrp bezei INTO TABLE lt_tvgrt FROM tvgrt FOR ALL ENTRIES IN ZSDFB_YSHDDCX WHERE vkgrp ZSDFB_YSHDDCX-vkgrp. 循環(huán)內(nèi)用 READ TABLE 匹配 LOOP AT ZSDFB_YSHDDCX INTO wa_YSHDDCX. READ TABLE lt_kna1 INTO DATA(ls_kna1) WITH KEY kunnr wa_YSHDDCX-kunnr. IF sy-subrc 0. wa_YSHDDCX-name1 ls_kna1-name1. ENDIF. READ TABLE lt_tvgrt INTO DATA(ls_tvgrt) WITH KEY vkgrp wa_YSHDDCX-vkgrp. IF sy-subrc 0. wa_YSHDDCX-bezei20 ls_tvgrt-bezei. ENDIF. MODIFY ZSDFB_YSHDDCX FROM wa_YSHDDCX. ENDLOOP.這段代碼的關(guān)鍵是FOR ALL ENTRIES IN ZSDFB_YSHDDCX它把內(nèi)表里的 KUNNR 和 VKGRP 作為查詢條件一次性從 KNA1 和 TVGRT 取回所有匹配記錄。注意FOR ALL ENTRIES要求驅(qū)動(dòng)內(nèi)表不能為空否則會(huì)全表掃描。所以在執(zhí)行前要檢查IF ZSDFB_YSHDDCX IS NOT INITIAL.。另外READ TABLE的WITH KEY要用內(nèi)表的主鍵或排序字段如果內(nèi)表沒排序可以先SORT ZSDFB_YSHDDCX BY kunnr vkgrp.再循環(huán)。5.3 優(yōu)化前后的性能對(duì)比與驗(yàn)證方法優(yōu)化前假設(shè)內(nèi)表有 100 行每行執(zhí)行 6 個(gè)select single數(shù)據(jù)庫(kù)交互 600 次。優(yōu)化后6 個(gè)批量查詢各執(zhí)行一次數(shù)據(jù)庫(kù)交互 6 次加上循環(huán)內(nèi)的read table內(nèi)存操作整體響應(yīng)時(shí)間從秒級(jí)降到毫秒級(jí)。驗(yàn)證方法在 Event Handler 里加GET RUN TIME FIELD記錄開始和結(jié)束時(shí)間輸出到errormsg或日志里對(duì)比優(yōu)化前后的耗時(shí)。也可以在 ST05 里開 SQL Trace看數(shù)據(jù)庫(kù)交互次數(shù)是否明顯減少。我一般會(huì)在開發(fā)階段就用FOR ALL ENTRIES替代循環(huán)內(nèi)select single因?yàn)楹笃跀?shù)據(jù)量增長(zhǎng)是不可控的。從那以后我每次寫 BSP 報(bào)表只要涉及循環(huán)內(nèi)取數(shù)都強(qiáng)制走一遍“能不能批量查”的檢查避免上線后被業(yè)務(wù)追著優(yōu)化。希望幫到你。本文還有配套的精品資源點(diǎn)擊獲取