ABAP核心进阶篇(120篇):ALV报表开发(15篇)
功能进阶篇(6篇)
第四篇:ALV数据导出增强------实现Excel/PDF/CSV多格式自定义导出
博客标题:《ALV数据导出增强:实现Excel/PDF/CSV多格式自定义导出》
博客简介:详解ALV报表的多格式导出方案,覆盖标准导出功能增强、自定义Excel模板填充、带样式PDF文件生成、批量导出多页报表的实现代码,解决默认导出格式混乱、无法满足业务归档要求的问题。
📖 写在前面
ALV报表的标准导出功能虽然方便,但往往无法满足业务的个性化需求:默认Excel格式混乱、PDF无法满足归档要求、CSV中文乱码、无法批量导出多页报表。本文将详细讲解四种常用的专业导出方案,帮助你彻底解决这些痛点。
#mermaid-svg-xNUglVEV6np5rQzu{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-xNUglVEV6np5rQzu .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-xNUglVEV6np5rQzu .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-xNUglVEV6np5rQzu .error-icon{fill:#552222;}#mermaid-svg-xNUglVEV6np5rQzu .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-xNUglVEV6np5rQzu .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-xNUglVEV6np5rQzu .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-xNUglVEV6np5rQzu .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-xNUglVEV6np5rQzu .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-xNUglVEV6np5rQzu .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-xNUglVEV6np5rQzu .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-xNUglVEV6np5rQzu .marker{fill:#333333;stroke:#333333;}#mermaid-svg-xNUglVEV6np5rQzu .marker.cross{stroke:#333333;}#mermaid-svg-xNUglVEV6np5rQzu svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-xNUglVEV6np5rQzu p{margin:0;}#mermaid-svg-xNUglVEV6np5rQzu .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-xNUglVEV6np5rQzu .cluster-label text{fill:#333;}#mermaid-svg-xNUglVEV6np5rQzu .cluster-label span{color:#333;}#mermaid-svg-xNUglVEV6np5rQzu .cluster-label span p{background-color:transparent;}#mermaid-svg-xNUglVEV6np5rQzu .label text,#mermaid-svg-xNUglVEV6np5rQzu span{fill:#333;color:#333;}#mermaid-svg-xNUglVEV6np5rQzu .node rect,#mermaid-svg-xNUglVEV6np5rQzu .node circle,#mermaid-svg-xNUglVEV6np5rQzu .node ellipse,#mermaid-svg-xNUglVEV6np5rQzu .node polygon,#mermaid-svg-xNUglVEV6np5rQzu .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-xNUglVEV6np5rQzu .rough-node .label text,#mermaid-svg-xNUglVEV6np5rQzu .node .label text,#mermaid-svg-xNUglVEV6np5rQzu .image-shape .label,#mermaid-svg-xNUglVEV6np5rQzu .icon-shape .label{text-anchor:middle;}#mermaid-svg-xNUglVEV6np5rQzu .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-xNUglVEV6np5rQzu .rough-node .label,#mermaid-svg-xNUglVEV6np5rQzu .node .label,#mermaid-svg-xNUglVEV6np5rQzu .image-shape .label,#mermaid-svg-xNUglVEV6np5rQzu .icon-shape .label{text-align:center;}#mermaid-svg-xNUglVEV6np5rQzu .node.clickable{cursor:pointer;}#mermaid-svg-xNUglVEV6np5rQzu .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-xNUglVEV6np5rQzu .arrowheadPath{fill:#333333;}#mermaid-svg-xNUglVEV6np5rQzu .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-xNUglVEV6np5rQzu .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-xNUglVEV6np5rQzu .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-xNUglVEV6np5rQzu .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-xNUglVEV6np5rQzu .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-xNUglVEV6np5rQzu .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-xNUglVEV6np5rQzu .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-xNUglVEV6np5rQzu .cluster text{fill:#333;}#mermaid-svg-xNUglVEV6np5rQzu .cluster span{color:#333;}#mermaid-svg-xNUglVEV6np5rQzu div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-xNUglVEV6np5rQzu .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-xNUglVEV6np5rQzu rect.text{fill:none;stroke-width:0;}#mermaid-svg-xNUglVEV6np5rQzu .icon-shape,#mermaid-svg-xNUglVEV6np5rQzu .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-xNUglVEV6np5rQzu .icon-shape p,#mermaid-svg-xNUglVEV6np5rQzu .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-xNUglVEV6np5rQzu .icon-shape .label rect,#mermaid-svg-xNUglVEV6np5rQzu .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-xNUglVEV6np5rQzu .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-xNUglVEV6np5rQzu .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-xNUglVEV6np5rQzu :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 数据导出方案
📋 标准ALV导出
工具栏/编程调用
📊 自定义Excel (OLE2)
带样式/公式/图表
📄 PDF归档导出
Smart Forms + CONVERT_OTF
📃 CSV数据交换
GUI_DOWNLOAD + 编码处理
快速导出 / 无复杂样式
专业报表 / 需样式与公式
打印归档 / 页眉页脚
系统对接 / 数据交换
通过本文的学习,你将掌握:
- 标准ALV导出功能的增强方法(工具栏自定义/编程调用)
- 自定义OLE2 Excel导出(带样式、公式、单元格合并)
- PDF文件生成(Smart Forms + CONVERT_OTF)
- CSV导出及中文编码完美处理
- 各方案的适用场景与选型建议
一、导出方案对比与决策
🆚 1.1 四种方案能力矩阵
| 方案 | 样式能力 | 性能 | 前端依赖 | 归档级别 | 适用场景 |
|---|---|---|---|---|---|
| 标准ALV导出 | 基础 | 快 | 无 | 无 | 快速导出、数据检查 |
| OLE2 Excel | 极强 | 慢 | 需安装Excel | 中 | 复杂报表、领导汇报 |
| PDF (Smart Forms) | 强 | 中 | 无 | 高 | 归档、打印、邮件附件 |
| CSV | 无 | 极快 | 无 | 无 | 数据交换、批量导入 |
🔀 1.2 方案选型决策
#mermaid-svg-gbawrTEfgM5tsJxG{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-gbawrTEfgM5tsJxG .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-gbawrTEfgM5tsJxG .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-gbawrTEfgM5tsJxG .error-icon{fill:#552222;}#mermaid-svg-gbawrTEfgM5tsJxG .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-gbawrTEfgM5tsJxG .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-gbawrTEfgM5tsJxG .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-gbawrTEfgM5tsJxG .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-gbawrTEfgM5tsJxG .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-gbawrTEfgM5tsJxG .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-gbawrTEfgM5tsJxG .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-gbawrTEfgM5tsJxG .marker{fill:#333333;stroke:#333333;}#mermaid-svg-gbawrTEfgM5tsJxG .marker.cross{stroke:#333333;}#mermaid-svg-gbawrTEfgM5tsJxG svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-gbawrTEfgM5tsJxG p{margin:0;}#mermaid-svg-gbawrTEfgM5tsJxG .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-gbawrTEfgM5tsJxG .cluster-label text{fill:#333;}#mermaid-svg-gbawrTEfgM5tsJxG .cluster-label span{color:#333;}#mermaid-svg-gbawrTEfgM5tsJxG .cluster-label span p{background-color:transparent;}#mermaid-svg-gbawrTEfgM5tsJxG .label text,#mermaid-svg-gbawrTEfgM5tsJxG span{fill:#333;color:#333;}#mermaid-svg-gbawrTEfgM5tsJxG .node rect,#mermaid-svg-gbawrTEfgM5tsJxG .node circle,#mermaid-svg-gbawrTEfgM5tsJxG .node ellipse,#mermaid-svg-gbawrTEfgM5tsJxG .node polygon,#mermaid-svg-gbawrTEfgM5tsJxG .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-gbawrTEfgM5tsJxG .rough-node .label text,#mermaid-svg-gbawrTEfgM5tsJxG .node .label text,#mermaid-svg-gbawrTEfgM5tsJxG .image-shape .label,#mermaid-svg-gbawrTEfgM5tsJxG .icon-shape .label{text-anchor:middle;}#mermaid-svg-gbawrTEfgM5tsJxG .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-gbawrTEfgM5tsJxG .rough-node .label,#mermaid-svg-gbawrTEfgM5tsJxG .node .label,#mermaid-svg-gbawrTEfgM5tsJxG .image-shape .label,#mermaid-svg-gbawrTEfgM5tsJxG .icon-shape .label{text-align:center;}#mermaid-svg-gbawrTEfgM5tsJxG .node.clickable{cursor:pointer;}#mermaid-svg-gbawrTEfgM5tsJxG .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-gbawrTEfgM5tsJxG .arrowheadPath{fill:#333333;}#mermaid-svg-gbawrTEfgM5tsJxG .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-gbawrTEfgM5tsJxG .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-gbawrTEfgM5tsJxG .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-gbawrTEfgM5tsJxG .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-gbawrTEfgM5tsJxG .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-gbawrTEfgM5tsJxG .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-gbawrTEfgM5tsJxG .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-gbawrTEfgM5tsJxG .cluster text{fill:#333;}#mermaid-svg-gbawrTEfgM5tsJxG .cluster span{color:#333;}#mermaid-svg-gbawrTEfgM5tsJxG div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-gbawrTEfgM5tsJxG .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-gbawrTEfgM5tsJxG rect.text{fill:none;stroke-width:0;}#mermaid-svg-gbawrTEfgM5tsJxG .icon-shape,#mermaid-svg-gbawrTEfgM5tsJxG .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-gbawrTEfgM5tsJxG .icon-shape p,#mermaid-svg-gbawrTEfgM5tsJxG .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-gbawrTEfgM5tsJxG .icon-shape .label rect,#mermaid-svg-gbawrTEfgM5tsJxG .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-gbawrTEfgM5tsJxG .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-gbawrTEfgM5tsJxG .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-gbawrTEfgM5tsJxG :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 否
是
否
是
是
否
需要导出数据
需要格式化样式?
数据交换/批量导入?
CSV 导出 (UTF-8 BOM)
标准 ALV 导出
需要打印归档或
PDF电子签名?
PDF 导出 (Smart Forms)
OLE2 Excel 导出
二、方案一:标准ALV导出增强
📋 2.1 适用场景
快速导出,不需要复杂样式,开发成本最低。可通过编程方式触发导出,避免用户手动操作。
📋 2.2 OO ALV 编程触发导出(推荐)
原理 :通过模拟ALV工具栏功能码触发标准导出对话框,比直接调用 call_function 更稳定。
abap
" 触发标准 Excel 导出(XXL 格式)
go_alv->get_function( )->set_function( i_fcode = '&XXL' ).
go_alv->get_function( )->trigger_function( i_fcode = '&XXL' ).
" 触发标准 CSV 导出
go_alv->get_function( )->set_function( i_fcode = '&CSV' ).
go_alv->get_function( )->trigger_function( i_fcode = '&CSV' ).
" 触发标准 PDF 导出(需要安装 ADS 或配置前端PDF打印机)
go_alv->get_function( )->set_function( i_fcode = '&PDF' ).
go_alv->get_function( )->trigger_function( i_fcode = '&PDF' ).
注意:上述代码会调出系统的标准文件保存对话框,用户体验与点击工具栏按钮一致,最为可靠。
三、方案二:自定义Excel导出(OLE2)
📊 3.1 适用场景
需要复杂样式、公式、合并单元格的专业Excel报表,适合汇报材料。
📊 3.2 完整代码示例(已优化资源释放)
abap
REPORT zmm_po_export_ole2.
TYPES: BEGIN OF ty_po_data,
ebeln TYPE ekko-ebeln,
erdat TYPE ekko-erdat,
lifnr TYPE ekko-lifnr,
name1 TYPE lfa1-name1,
matnr TYPE ekpo-matnr,
maktx TYPE makt-maktx,
menge TYPE ekpo-menge,
meins TYPE ekpo-meins,
netwr TYPE ekpo-netwr,
END OF ty_po_data.
DATA: gt_po_data TYPE TABLE OF ty_po_data.
DATA: go_excel TYPE ole2_object,
go_workbooks TYPE ole2_object,
go_workbook TYPE ole2_object,
go_sheets TYPE ole2_object,
go_sheet TYPE ole2_object,
go_range TYPE ole2_object,
go_font TYPE ole2_object,
go_borders TYPE ole2_object.
PARAMETERS: p_ebeln TYPE ekko-ebeln.
START-OF-SELECTION.
PERFORM fetch_data.
PERFORM create_excel.
PERFORM fill_excel_data.
PERFORM format_excel.
PERFORM save_and_show.
PERFORM free_all_objects. " 统一释放
FORM fetch_data.
SELECT ekko~ebeln, ekko~erdat, ekko~lifnr, lfa1~name1,
ekpo~matnr, makt~maktx, ekpo~menge, ekpo~meins, ekpo~netwr
FROM ekko
INNER JOIN ekpo ON ekko~ebeln = ekpo~ebeln
LEFT JOIN lfa1 ON ekko~lifnr = lfa1~lifnr
LEFT JOIN makt ON ekpo~matnr = makt~matnr AND makt~spras = sy-langu
INTO CORRESPONDING FIELDS OF TABLE gt_po_data
WHERE ( p_ebeln IS INITIAL OR ekko~ebeln = p_ebeln )
UP TO 50 ROWS.
ENDFORM.
FORM create_excel.
CREATE OBJECT go_excel 'Excel.Application'.
SET PROPERTY OF go_excel 'Visible' = 0.
GET PROPERTY OF go_excel 'Workbooks' = go_workbooks.
CALL METHOD OF go_workbooks 'Add' = go_workbook.
GET PROPERTY OF go_workbook 'Sheets' = go_sheets.
CALL METHOD OF go_sheets 'Item' = go_sheet EXPORTING #1 = 1.
ENDFORM.
FORM fill_excel_data.
DATA: lv_row TYPE i VALUE 1.
" 标题
PERFORM set_cell_value USING lv_row 1 '采购订单报表'.
lv_row = lv_row + 2.
" 表头
PERFORM set_cell_value USING lv_row 1 '采购订单号'.
PERFORM set_cell_value USING lv_row 2 '创建日期'.
PERFORM set_cell_value USING lv_row 3 '供应商编号'.
PERFORM set_cell_value USING lv_row 4 '供应商名称'.
PERFORM set_cell_value USING lv_row 5 '物料编号'.
PERFORM set_cell_value USING lv_row 6 '物料描述'.
PERFORM set_cell_value USING lv_row 7 '数量'.
PERFORM set_cell_value USING lv_row 8 '单位'.
PERFORM set_cell_value USING lv_row 9 '金额'.
lv_row = lv_row + 1.
" 数据行
LOOP AT gt_po_data INTO DATA(ls_po).
PERFORM set_cell_value USING lv_row 1 ls_po-ebeln.
PERFORM set_cell_value USING lv_row 2 ls_po-erdat.
PERFORM set_cell_value USING lv_row 3 ls_po-lifnr.
PERFORM set_cell_value USING lv_row 4 ls_po-name1.
PERFORM set_cell_value USING lv_row 5 ls_po-matnr.
PERFORM set_cell_value USING lv_row 6 ls_po-maktx.
PERFORM set_cell_value USING lv_row 7 ls_po-menge.
PERFORM set_cell_value USING lv_row 8 ls_po-meins.
PERFORM set_cell_value USING lv_row 9 ls_po-netwr.
lv_row = lv_row + 1.
ENDLOOP.
" 合计行
PERFORM set_cell_value USING lv_row 1 '合计'.
PERFORM set_cell_formula USING lv_row 9
|SUM(I4:I{ lv_row - 1 })|.
ENDFORM.
FORM set_cell_value USING iv_row TYPE i iv_col TYPE i iv_value TYPE any.
CALL METHOD OF go_sheet 'Cells' = go_range EXPORTING #1 = iv_row #2 = iv_col.
SET PROPERTY OF go_range 'Value' = iv_value.
ENDFORM.
FORM set_cell_formula USING iv_row TYPE i iv_col TYPE i iv_formula TYPE string.
CALL METHOD OF go_sheet 'Cells' = go_range EXPORTING #1 = iv_row #2 = iv_col.
SET PROPERTY OF go_range 'Formula' = iv_formula.
ENDFORM.
FORM format_excel.
" 合并标题并居中加粗
CALL METHOD OF go_sheet 'Range' = go_range EXPORTING #1 = 'A1:I1'.
SET PROPERTY OF go_range 'MergeCells' = 1.
SET PROPERTY OF go_range 'HorizontalAlignment' = -4108. " xlCenter
GET PROPERTY OF go_range 'Font' = go_font.
SET PROPERTY OF go_font 'Bold' = 1.
SET PROPERTY OF go_font 'Size' = 16.
" 表头加粗
CALL METHOD OF go_sheet 'Range' = go_range EXPORTING #1 = 'A3:I3'.
GET PROPERTY OF go_range 'Font' = go_font.
SET PROPERTY OF go_font 'Bold' = 1.
" 数据区域加边框
CALL METHOD OF go_sheet 'Range' = go_range
EXPORTING #1 = |A3:I{ lines( gt_po_data ) + 3 }|.
GET PROPERTY OF go_range 'Borders' = go_borders.
SET PROPERTY OF go_borders 'LineStyle' = 1. " xlContinuous
" 自动列宽
CALL METHOD OF go_sheet 'Columns' = go_range.
CALL METHOD OF go_range 'AutoFit'.
ENDFORM.
FORM save_and_show.
CALL METHOD OF go_workbook 'SaveAs'
EXPORTING #1 = 'C:\Temp\采购订单报表.xlsx' #2 = 51. " xlOpenXMLWorkbook
SET PROPERTY OF go_excel 'Visible' = 1.
MESSAGE 'Excel文件已生成' TYPE 'S'.
ENDFORM.
FORM free_all_objects.
FREE OBJECT go_borders.
FREE OBJECT go_font.
FREE OBJECT go_range.
FREE OBJECT go_sheet.
FREE OBJECT go_sheets.
FREE OBJECT go_workbook.
FREE OBJECT go_workbooks.
FREE OBJECT go_excel.
ENDFORM.
📊 3.3 核心要点与限制
- OLE2 必须在有Excel的前端环境运行,后台作业无法使用。
- 务必按从属关系逆序释放所有OLE2对象,否则Excel进程残留。
- 大量数据(>1000行)性能极差,建议改用
ABAP2XLSX类库或后台SMARTFORMS导出。
四、方案三:PDF归档导出(Smart Forms + CONVERT_OTF)
📄 4.1 适用场景
需要正式归档、打印、邮件附件,要求具备页眉页脚、水印、公司Logo。
📄 4.2 完整流程与代码
前置条件 :使用事务 SMARTFORMS 创建表单 ZMM_PO_REPORT,定义表接口 GT_PO_DATA,设计表格和文本。
abap
REPORT zmm_po_export_pdf.
TYPES: BEGIN OF ty_po_data,
ebeln TYPE ekko-ebeln,
erdat TYPE ekko-erdat,
name1 TYPE lfa1-name1,
maktx TYPE makt-maktx,
menge TYPE ekpo-menge,
netwr TYPE ekpo-netwr,
END OF ty_po_data.
DATA: gt_po_data TYPE TABLE OF ty_po_data,
gt_otf TYPE TABLE OF itcoo,
gt_pdf TYPE solix_tab,
lv_len TYPE i.
PARAMETERS: p_ebeln TYPE ekko-ebeln.
START-OF-SELECTION.
PERFORM fetch_data.
PERFORM call_smartform.
PERFORM convert_otf_to_pdf.
PERFORM download_pdf.
FORM fetch_data.
SELECT ekko~ebeln, ekko~erdat, lfa1~name1,
makt~maktx, ekpo~menge, ekpo~netwr
FROM ekko
INNER JOIN ekpo ON ekko~ebeln = ekpo~ebeln
LEFT JOIN lfa1 ON ekko~lifnr = lfa1~lifnr
LEFT JOIN makt ON ekpo~matnr = makt~matnr AND makt~spras = sy-langu
INTO CORRESPONDING FIELDS OF TABLE gt_po_data
WHERE ( p_ebeln IS INITIAL OR ekko~ebeln = p_ebeln )
UP TO 100 ROWS.
ENDFORM.
FORM call_smartform.
DATA: lv_fm_name TYPE rs38l_fnam,
ls_output TYPE ssfcrescl.
CALL FUNCTION 'SSF_FUNCTION_MODULE_NAME'
EXPORTING
formname = 'ZMM_PO_REPORT'
IMPORTING
fm_name = lv_fm_name
EXCEPTIONS
no_form = 1
OTHERS = 2.
IF sy-subrc <> 0.
MESSAGE '请先通过SMARTFORMS创建ZMM_PO_REPORT' TYPE 'E'.
ENDIF.
CALL FUNCTION lv_fm_name
EXPORTING
control_parameters = VALUE ssfctrlop( no_dialog = 'X' getotf = 'X' )
IMPORTING
output = ls_output
TABLES
gt_po_data = gt_po_data
EXCEPTIONS
formatting_error = 1
internal_error = 2
OTHERS = 3.
gt_otf = ls_output-otfdata.
ENDFORM.
FORM convert_otf_to_pdf.
CALL FUNCTION 'CONVERT_OTF'
EXPORTING
format = 'PDF'
max_linewidth = 132
IMPORTING
bin_filesize = lv_len
TABLES
otf = gt_otf
lines = gt_pdf
EXCEPTIONS
err_conv_not_possible = 1
OTHERS = 2.
ENDFORM.
FORM download_pdf.
cl_gui_frontend_services=>gui_download(
EXPORTING
filename = 'C:\Temp\采购订单报表.pdf'
filetype = 'BIN'
size = lv_len
CHANGING
data_tab = gt_pdf ).
MESSAGE 'PDF已生成' TYPE 'S'.
ENDFORM.
关键点 :Smart Forms 的表格接口名称必须与
TABLES参数后的内表名完全一致(此处为GT_PO_DATA)。
五、方案四:CSV导出(中文编码完美处理)
📃 5.1 适用场景
系统间数据交换、批量导入、分析工具对接。
📃 5.2 完整代码示例(UTF-8 BOM + 正确分隔符)
abap
REPORT zmm_po_export_csv.
TYPES: BEGIN OF ty_po_data,
ebeln TYPE ekko-ebeln,
erdat TYPE ekko-erdat,
name1 TYPE lfa1-name1,
maktx TYPE makt-maktx,
menge TYPE ekpo-menge,
netwr TYPE ekpo-netwr,
END OF ty_po_data.
DATA: gt_po_data TYPE TABLE OF ty_po_data,
gt_csv TYPE TABLE OF string,
gs_csv TYPE string.
PARAMETERS: p_ebeln TYPE ekko-ebeln.
START-OF-SELECTION.
PERFORM fetch_data.
PERFORM prepare_csv_data.
PERFORM download_csv_with_bom.
FORM fetch_data.
SELECT ekko~ebeln, ekko~erdat, lfa1~name1,
makt~maktx, ekpo~menge, ekpo~netwr
FROM ekko
INNER JOIN ekpo ON ekko~ebeln = ekpo~ebeln
LEFT JOIN lfa1 ON ekko~lifnr = lfa1~lifnr
LEFT JOIN makt ON ekpo~matnr = makt~matnr AND makt~spras = sy-langu
INTO CORRESPONDING FIELDS OF TABLE gt_po_data
WHERE ( p_ebeln IS INITIAL OR ekko~ebeln = p_ebeln )
UP TO 100 ROWS.
ENDFORM.
FORM prepare_csv_data.
" 表头
gs_csv = |采购订单号,创建日期,供应商名称,物料描述,数量,金额|.
APPEND gs_csv TO gt_csv.
" 数据行(金额转换为字符串以避免科学计数法)
LOOP AT gt_po_data INTO DATA(ls_po).
gs_csv = |{ ls_po-ebeln },{ ls_po-erdat },{ ls_po-name1 },{ ls_po-maktx },{ ls_po-menge },{ ls_po-netwr }|.
APPEND gs_csv TO gt_csv.
ENDLOOP.
ENDFORM.
FORM download_csv_with_bom.
DATA: lv_bom_hex TYPE xstring VALUE 'EFBBBF', " UTF-8 BOM
lv_content TYPE string,
lv_hex TYPE xstring,
lv_full_hex TYPE xstring,
lt_binary TYPE solix_tab.
" 将所有行用回车换行连接
LOOP AT gt_csv INTO lv_content.
IF sy-tabix > 1.
CONCATENATE lv_content cl_abap_char_utilities=>cr_lf INTO lv_content.
ENDIF.
CALL FUNCTION 'SCMS_STRING_TO_XSTRING'
EXPORTING
text = lv_content
mimetype = 'text/csv'
IMPORTING
buffer = lv_hex.
CONCATENATE lv_full_hex lv_hex INTO lv_full_hex IN BYTE MODE.
ENDLOOP.
" 前面加上 BOM
CONCATENATE lv_bom_hex lv_full_hex INTO lv_full_hex IN BYTE MODE.
" 转换为 SOLIX 表
CALL FUNCTION 'SCMS_XSTRING_TO_BINARY'
EXPORTING
buffer = lv_full_hex
TABLES
binary_tab = lt_binary.
cl_gui_frontend_services=>gui_download(
EXPORTING
filename = 'C:\Temp\采购订单报表.csv'
filetype = 'BIN'
CHANGING
data_tab = lt_binary ).
MESSAGE 'CSV已生成(UTF-8 BOM)' TYPE 'S'.
ENDFORM.
核心 :通过手动写入 UTF-8 BOM(
EFBBBF)并转为二进制下载,确保 Excel 打开时中文不再乱码。
六、常见问题与排查
| # | 问题 | 原因 | 解决方法 |
|---|---|---|---|
| Q1 | OLE2 导出后 Excel 进程残留 | 对象未完全释放 | 使用逆序 FREE OBJECT 并检查所有引用 |
| Q2 | PDF 导出内容为空 | Smart Forms 表格接口名不匹配 | 确保 TABLES 后的参数名与表单定义一致 |
| Q3 | CSV 中文在 Excel 中乱码 | 缺少 UTF-8 BOM | 按上文方案写入 BOM 并以二进制格式下载 |
| Q4 | CSV 金额显示为科学计数 | Excel 自动转换 | 金额字段添加制表符前缀或导出时加引号 |
| Q5 | OO ALV call_function 无反应 |
该方法不稳定 | 改用 get_function( )->trigger_function( ) 方式 |
七、总结
#mermaid-svg-n7x6qsmh3NVon4yP{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-n7x6qsmh3NVon4yP .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-n7x6qsmh3NVon4yP .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-n7x6qsmh3NVon4yP .error-icon{fill:#552222;}#mermaid-svg-n7x6qsmh3NVon4yP .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-n7x6qsmh3NVon4yP .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-n7x6qsmh3NVon4yP .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-n7x6qsmh3NVon4yP .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-n7x6qsmh3NVon4yP .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-n7x6qsmh3NVon4yP .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-n7x6qsmh3NVon4yP .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-n7x6qsmh3NVon4yP .marker{fill:#333333;stroke:#333333;}#mermaid-svg-n7x6qsmh3NVon4yP .marker.cross{stroke:#333333;}#mermaid-svg-n7x6qsmh3NVon4yP svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-n7x6qsmh3NVon4yP p{margin:0;}#mermaid-svg-n7x6qsmh3NVon4yP .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-n7x6qsmh3NVon4yP .cluster-label text{fill:#333;}#mermaid-svg-n7x6qsmh3NVon4yP .cluster-label span{color:#333;}#mermaid-svg-n7x6qsmh3NVon4yP .cluster-label span p{background-color:transparent;}#mermaid-svg-n7x6qsmh3NVon4yP .label text,#mermaid-svg-n7x6qsmh3NVon4yP span{fill:#333;color:#333;}#mermaid-svg-n7x6qsmh3NVon4yP .node rect,#mermaid-svg-n7x6qsmh3NVon4yP .node circle,#mermaid-svg-n7x6qsmh3NVon4yP .node ellipse,#mermaid-svg-n7x6qsmh3NVon4yP .node polygon,#mermaid-svg-n7x6qsmh3NVon4yP .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-n7x6qsmh3NVon4yP .rough-node .label text,#mermaid-svg-n7x6qsmh3NVon4yP .node .label text,#mermaid-svg-n7x6qsmh3NVon4yP .image-shape .label,#mermaid-svg-n7x6qsmh3NVon4yP .icon-shape .label{text-anchor:middle;}#mermaid-svg-n7x6qsmh3NVon4yP .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-n7x6qsmh3NVon4yP .rough-node .label,#mermaid-svg-n7x6qsmh3NVon4yP .node .label,#mermaid-svg-n7x6qsmh3NVon4yP .image-shape .label,#mermaid-svg-n7x6qsmh3NVon4yP .icon-shape .label{text-align:center;}#mermaid-svg-n7x6qsmh3NVon4yP .node.clickable{cursor:pointer;}#mermaid-svg-n7x6qsmh3NVon4yP .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-n7x6qsmh3NVon4yP .arrowheadPath{fill:#333333;}#mermaid-svg-n7x6qsmh3NVon4yP .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-n7x6qsmh3NVon4yP .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-n7x6qsmh3NVon4yP .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-n7x6qsmh3NVon4yP .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-n7x6qsmh3NVon4yP .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-n7x6qsmh3NVon4yP .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-n7x6qsmh3NVon4yP .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-n7x6qsmh3NVon4yP .cluster text{fill:#333;}#mermaid-svg-n7x6qsmh3NVon4yP .cluster span{color:#333;}#mermaid-svg-n7x6qsmh3NVon4yP div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-n7x6qsmh3NVon4yP .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-n7x6qsmh3NVon4yP rect.text{fill:none;stroke-width:0;}#mermaid-svg-n7x6qsmh3NVon4yP .icon-shape,#mermaid-svg-n7x6qsmh3NVon4yP .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-n7x6qsmh3NVon4yP .icon-shape p,#mermaid-svg-n7x6qsmh3NVon4yP .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-n7x6qsmh3NVon4yP .icon-shape .label rect,#mermaid-svg-n7x6qsmh3NVon4yP .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-n7x6qsmh3NVon4yP .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-n7x6qsmh3NVon4yP .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-n7x6qsmh3NVon4yP :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 导出方案
标准ALV
开发成本最低
OLE2 Excel
样式能力最强
PDF
归档最规范
CSV
交换最通用
触发功能码 &XXL &CSV
释放所有OLE对象
Smart Forms + CONVERT_OTF
UTF-8 BOM + 二进制下载
| 方案 | 核心技术 | 一句话要点 |
|---|---|---|
| 标准ALV | trigger_function |
最稳定,适合日常快速导出 |
| OLE2 Excel | OLE2 对象模型 | 释放所有对象,仅限前台 |
| Smart Forms + OTF | 表格接口名必须一致 | |
| CSV | BOM + 二进制 | 写入BOM解决乱码 |
核心要点:
- 根据场景选择:简单导出用标准功能,复杂报表用 OLE2,归档用 PDF,数据交换用 CSV
- OLE2 务必逆序释放所有对象
- PDF 需提前创建 Smart Forms 并确保接口参数名称匹配
- CSV 必须写入 BOM 并转为二进制下载,彻底告别乱码
- 批量导出建议限制数据量并采用异步处理
下一篇预告:《ALV异常场景兼容:空数据、权限不足、数据报错的友好提示实现》
作者 :爱喝水的鱼丶
版本记录 :2026年7月
验证基准:SAP NetWeaver 7.51
💬 你在开发导出功能时遇到过哪些奇怪的格式问题?欢迎分享你的经验!