SAP-ABAP:ALV数据导出增强——实现Excel/PDF/CSV多格式自定义导出


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 对象模型 释放所有对象,仅限前台
PDF Smart Forms + OTF 表格接口名必须一致
CSV BOM + 二进制 写入BOM解决乱码

核心要点

  1. 根据场景选择:简单导出用标准功能,复杂报表用 OLE2,归档用 PDF,数据交换用 CSV
  2. OLE2 务必逆序释放所有对象
  3. PDF 需提前创建 Smart Forms 并确保接口参数名称匹配
  4. CSV 必须写入 BOM 并转为二进制下载,彻底告别乱码
  5. 批量导出建议限制数据量并采用异步处理

下一篇预告:《ALV异常场景兼容:空数据、权限不足、数据报错的友好提示实现》

作者 :爱喝水的鱼丶

版本记录 :2026年7月

验证基准:SAP NetWeaver 7.51

💬 你在开发导出功能时遇到过哪些奇怪的格式问题?欢迎分享你的经验!

相关推荐
不会c+1 小时前
C语言:入门到精通(408考研版)系列七 派生的数据类型
c语言·开发语言
不定积分要+C_yyy2 小时前
Java基础核心精讲:基本数据类型与包装类区别、自动装箱拆箱与缓存陷阱实战解析
java·开发语言
xingxiliang2 小时前
ReliableAgent:类似工程级可用的agent示例
java·开发语言·agent
buhuizhiyuci2 小时前
【python篇——一周速通python语法】python的基础语法操作
开发语言·python
程序猿编码2 小时前
没有AI框架,没有GPU,一个C语言文件跑通大模型
c语言·开发语言·人工智能·深度学习·ai·大模型
zmzb01032 小时前
C++课后习题训练记录Day160
开发语言·c++
沫璃染墨2 小时前
现代C++⊂C++11篇(一)列表初始化全解 & std::initializer_list
开发语言·c++
阿米亚波2 小时前
【C++ STL】std::forward_list
开发语言·c++·笔记·stl·visual studio·forward_list
buhuizhiyuci3 小时前
【python篇——一周速通python语法】python的条件语句和循环语句
开发语言·python