ABAP ALV 从入门到精通

ALV(SAP List Viewer)是 SAP 中最常用的报表展示框架,几乎贯穿所有业务模块的报表开发。本文从零基础出发,由浅入深系统讲解 ALV 的全部知识点,涵盖 Function ALV、OO ALV、SALV 三种实现方式,并附完整可运行代码示例。


第一部分:入门基础

1.1 什么是 ALV

ALV 全称 SAP List Viewer,是 SAP 提供的一套标准化报表输出框架。它本质上是一个网格控件,能够将内表数据以表格形式展示,并内置了排序、筛选、汇总、导出 Excel 等常用功能。

为什么用 ALV 而不用 WRITE 语句?

对比项 WRITE 语句 ALV
格式控制 手动对齐,维护困难 自动列对齐,美观
排序/筛选 需自己写逻辑 内置一键操作
汇总/小计 需自己计算 内置自动汇总
导出 Excel 需自己写逻辑 内置一键导出
可编辑 不支持 支持单元格编辑
开发效率

1.2 ALV 家族概览

SAP 中 ALV 有三种实现方式,按推荐程度排序:

方式 核心对象 适用场景 推荐度
Function ALV REUSE_ALV_GRID_DISPLAY 等 FM 快速开发,简单报表 ⭐⭐⭐
OO ALV CL_GUI_ALV_GRID 需要精细事件控制 ⭐⭐⭐⭐
SALV CL_SALV_TABLE 新开发首选,SAP 推荐 ⭐⭐⭐⭐⭐

初学者建议:先掌握 Function ALV 理解核心概念,再学 OO ALV 理解事件机制,最后学 SALV 作为日常开发首选。

1.3 ALV 报表的标准开发步骤

无论哪种方式,ALV 报表开发都遵循以下 4 步:

第1步: 定义数据 → 内表 + 工作区
第2步: 取数填充 → SELECT 从数据库取数据填入内表
第3步: 设置显示属性 → Fieldcat(字段目录) + Layout(布局)
第4步: 调用ALV显示 → FM/方法 调用输出

1.4 第一个 ALV 程序(最简示例)

以下是最简单的 ALV 程序,只需要不到 30 行代码:

REPORT zdemo_alv_01.

* 第1步: 定义内表和工作区
TYPES: BEGIN OF ty_sflight,
carrid TYPE sflight-carrid, "航空公司
connid TYPE sflight-connid, "航班号
fldate TYPE sflight-fldate, "日期
price TYPE sflight-price, "价格
currency TYPE sflight-currency, "货币
seatsmax TYPE sflight-seatsmax, "最大座位
seatsocc TYPE sflight-seatsocc, "已占座位
END OF ty_sflight.

DATA: gt_flights TYPE STANDARD TABLE OF ty_sflight,
gs_flight TYPE ty_sflight.

* 第2步: 取数
SELECT carrid connid fldate price currency seatsmax seatsocc
FROM sflight
INTO TABLE gt_flights
UP TO 100 ROWS.

* 第3步: 调用 ALV 显示(最简方式,fieldcat 自动生成)
CALL FUNCTION 'REUSE_ALV_GRID_DISPLAY'
EXPORTING
i_structure_name = 'SFLIGHT' "参照字典结构自动生成字段目录
TABLES
t_outtab = gt_flights "要显示的内表
EXCEPTIONS
program_error = 1
OTHERS = 2.

IF sy-subrc <> 0.
MESSAGE 'ALV display error' TYPE 'E'.
ENDIF.

运行效果:执行后会显示一个带工具栏的表格,支持排序、筛选、合计、导出 Excel 等标准功能。

关键点说明

  • i_structure_name 指定参照的 DDIC 结构,系统自动根据结构生成 Fieldcat
  • t_outtab 是要显示的内表数据
  • 这是最简方式,不需要手动构建 Fieldcat 和 Layout

第二部分:Fieldcat 字段目录详解

Fieldcat(字段目录)是 ALV 的核心配置之一,它定义了每一列的显示属性:列标题、宽度、是否可编辑、是否汇总等。

2.1 两种构建方式

方式一:自动构建(REUSE_ALV_FIELDCATALOG_MERGE)

DATA: gt_fieldcat TYPE slis_t_fieldcat_alv.  "Function ALV 用

CALL FUNCTION 'REUSE_ALV_FIELDCATALOG_MERGE'
EXPORTING
i_program_name = sy-repid "当前程序名
i_internal_tabname = 'GT_FLIGHTS' "内表名称(大写)
i_inclname = sy-repid "包含程序名
i_bypassing_buffer = 'X' "跳过缓存
CHANGING
ct_fieldcat = gt_fieldcat
EXCEPTIONS
inconsistent_interface = 1
program_error = 2
OTHERS = 3.

注意:内表必须用 TYPES 定义结构,且字段名要与 DDIC 数据元素对应,才能自动带出文本描述。

方式二:手动构建

DATA: gt_fieldcat TYPE slis_t_fieldcat_alv,
gs_fieldcat TYPE slis_fieldcat_alv.

CLEAR gs_fieldcat.
gs_fieldcat-col_pos = 1. "列位置
gs_fieldcat-fieldname = 'CARRID'. "字段名
gs_fieldcat-tabname = 'GT_FLIGHTS'."内表名
gs_fieldcat-seltext_l = '航空公司'. "长文本
gs_fieldcat-outputlen = 10. "输出宽度
APPEND gs_fieldcat TO gt_fieldcat.

CLEAR gs_fieldcat.
gs_fieldcat-col_pos = 2.
gs_fieldcat-fieldname = 'CONNID'.
gs_fieldcat-tabname = 'GT_FLIGHTS'.
gs_fieldcat-seltext_l = '航班号'.
APPEND gs_fieldcat TO gt_fieldcat.

" ... 其他字段类似

方式三:混合方式(推荐)

先用 FM 自动生成,再手动修改需要调整的字段:

" 先自动构建
CALL FUNCTION 'REUSE_ALV_FIELDCATALOG_MERGE'
EXPORTING
i_program_name = sy-repid
i_internal_tabname = 'GT_FLIGHTS'
i_inclname = sy-repid
CHANGING
ct_fieldcat = gt_fieldcat.

" 再手动调整
LOOP AT gt_fieldcat INTO gs_fieldcat.
CASE gs_fieldcat-fieldname.
WHEN 'PRICE'.
gs_fieldcat-seltext_l = '票价'.
gs_fieldcat-do_sum = 'X'. "求和
gs_fieldcat-cfieldname = 'CURRENCY'. "货币参考字段
gs_fieldcat-outputlen = 12.
WHEN 'SEATSOCC'.
gs_fieldcat-seltext_l = '已占座位'.
gs_fieldcat-emphasize = 'C700'. "红色高亮
WHEN 'SEATSMAX'.
gs_fieldcat-seltext_l = '最大座位'.
ENDCASE.
MODIFY gt_fieldcat FROM gs_fieldcat.
ENDLOOP.

2.2 Fieldcat 常用属性速查表

属性 类型 说明 示例
fieldname CHAR 字段名(必须与内表字段一致) 'CARRID'
tabname CHAR 内表名 'GT_FLIGHTS'
col_pos INT 列位置(第几列) 1
seltext_l/m/s CHAR 列标题(长/中/短) '航空公司'
reptext_ddic CHAR 报表标题(优先级最高) '航空公司代码'
outputlen INT 输出宽度(字符数) 15
no_out CHAR 隐藏列(不显示) 'X'
tech CHAR 技术字段(完全隐藏) 'X'
edit CHAR 整列可编辑 'X'
checkbox CHAR 显示为复选框 'X'
hotspot CHAR 热点(单击触发事件) 'X'
do_sum CHAR 自动求和 'X'
no_sum CHAR 禁止求和 'X'
no_zero CHAR 前导零不显示 'X'
icon CHAR 显示图标 'X'
symbol CHAR 显示符号 'X'
emphasize CHAR 列颜色 'C300'(红色)
fix_column CHAR 固定列(冻结) 'X'
datatype CHAR 数据类型 'CURR'
ref_fieldname CHAR 参照字段(带出搜索帮助) 'MATNR'
ref_tabname CHAR 参照表 'MARA'
cfieldname CHAR 货币参考字段 'WAERS'
qfieldname CHAR 单位参考字段 'MEINS'
decimals_out INT 小数位数 2
ddictxt CHAR 使用 DDIC 文本 'L'
just CHAR 对齐方式 'L'/'R'/'C'
lzero CHAR 显示前导零 'X'
scrtext_l/m/s CHAR 屏幕文本(长/中/短) '航空公司'

2.3 OO ALV 的 Fieldcat(LVC 格式)

OO ALV 使用 LVC_T_FCAT 类型,字段略有不同:

DATA: gt_fcat TYPE lvc_t_fcat,
gs_fcat TYPE lvc_fcat.

" 自动构建(注意用 LVC 版本的 FM)
CALL FUNCTION 'LVC_FIELDCATALOG_MERGE'
EXPORTING
i_structure_name = 'SFLIGHT'
CHANGING
ct_fieldcat = gt_fcat.

" 手动构建示例
CLEAR gs_fcat.
gs_fcat-fieldname = 'CARRID'.
gs_fcat-coltext = '航空公司'. "LVC 用 coltext 代替 seltext_l
gs_fcat-outputlen = 10.
gs_fcat-fix_column = 'X'. "冻结列
APPEND gs_fcat TO gt_fcat.

SLIS 与 LVC Fieldcat 主要差异

属性 SLIS (Function ALV) LVC (OO ALV)
列标题 seltext_l coltext
内表名 tabname (不需要)
列颜色 emphasize emphasize
固定列 fix_column fix_column

第三部分:Layout 布局详解

Layout 控制 ALV 的整体外观:斑马纹、列宽优化、标题、选择模式等。

3.1 Function ALV 的 Layout

DATA: gs_layout TYPE slis_layout_alv.

" 斑马纹(奇偶行不同颜色)
gs_layout-zebra = 'X'.

" 列宽自动优化
gs_layout-colwidth_optimize = 'X'.

" 标题
gs_layout-window_titlebar = '航班信息查询'.

" 选择模式(单选/多选/无)
gs_layout-box_fieldname = 'SEL'. "内表中增加一个 SEL 字段作为选择列

" 不显示标题行
gs_layout-no_colhead = 'X'.

" 网格标题
gs_layout-grid_title = '航班列表'.

" 信息列(显示图标+文字)
gs_layout-info_fieldname = 'ROWCOLOR'. "内表中一个字段存行颜色

" 调用 ALV 时传入 Layout
CALL FUNCTION 'REUSE_ALV_GRID_DISPLAY'
EXPORTING
is_layout = gs_layout
it_fieldcat = gt_fieldcat
i_structure_name = 'SFLIGHT'
TABLES
t_outtab = gt_flights.

3.2 Layout 常用属性速查表

属性 说明 示例值
zebra 斑马纹 'X'
colwidth_optimize 列宽自动优化 'X'
window_titlebar 窗口标题 '查询结果'
grid_title 网格内标题 '航班列表'
box_fieldname 选择列字段名 'SEL'
no_colhead 隐藏列标题 'X'
no_vline 不显示垂直线 'X'
no_hline 不显示水平线 'X'
info_fieldname 行颜色字段 'ROWCOLOR'
coltab_fieldname 单元格颜色字段名 'CELLCOLOR'
edit 整个 ALV 可编辑 'X'
no_totline 不显示总计行 'X'
no_subtotline 不显示小计行 'X'
totals_text 总计文本 '合计'
subtotals_text 小计文本 '小计'
f2code 双击功能码 '&ETA'
detail_popup 明细弹窗 'X'
detail_titlebar 明细弹窗标题 '详细信息'
key_hotspot 主键字段热点 'X'
no_input 所有字段只读 'X'
numc_sum NUMC 字段求和 'X'

3.3 OO ALV 的 Layout(LVC 格式)

DATA: gs_layout_lvc TYPE lvc_s_layo.

gs_layout_lvc-zebra = 'X'.
gs_layout_lvc-cwidth_opt = 'X'. "注意:LVC 用 cwidth_opt
gs_layout_lvc-grid_title = '航班列表'.
gs_layout_lvc-sel_mode = 'A'. "选择模式: A=全选, B=多选, C=单选, D=无
gs_layout_lvc-ctab_fname = 'CELLCOLOR'."单元格颜色字段
gs_layout_lvc-stylefname = 'CELLSTYLES'."单元格样式字段(用于动态可编辑)
gs_layout_lvc-no_toolbar = 'X'. "隐藏工具栏

选择模式说明

说明
A 左侧全选按钮(默认,推荐)
B 多选(CheckBox 列)
C 单选(RadioButton 列)
D 无选择列

第四部分:排序、筛选与汇总

4.1 排序(Sort)

DATA: gt_sort TYPE slis_t_sortinfo_alv,
gs_sort TYPE slis_sortinfo_alv.

" 按航空公司排序
CLEAR gs_sort.
gs_sort-fieldname = 'CARRID'.
gs_sort-tabname = 'GT_FLIGHTS'.
gs_sort-up = 'X'. "升序
gs_sort-subtot = 'X'. "小计
APPEND gs_sort TO gt_sort.

" 按航班号排序(二级排序)
CLEAR gs_sort.
gs_sort-fieldname = 'CONNID'.
gs_sort-tabname = 'GT_FLIGHTS'.
gs_sort-up = 'X'.
APPEND gs_sort TO gt_sort.

" 传入 ALV
CALL FUNCTION 'REUSE_ALV_GRID_DISPLAY'
EXPORTING
it_sort = gt_sort
it_fieldcat = gt_fieldcat
TABLES
t_outtab = gt_flights.

4.2 筛选(Filter)

DATA: gt_filter TYPE slis_t_filter_alv,
gs_filter TYPE slis_filter_alv.

" 只显示价格大于 500 的记录
CLEAR gs_filter.
gs_filter-fieldname = 'PRICE'.
gs_filter-tabname = 'GT_FLIGHTS'.
gs_filter-sign0 = 'I'. "包含
gs_filter-opt0 = 'GT'. "大于
gs_filter-valuf_int = '500'.
APPEND gs_filter TO gt_filter.

4.3 汇总(Totals & Subtotals)

汇总有两种方式:

方式一:通过 Fieldcat 设置自动求和

" 对 PRICE 字段求和
gs_fieldcat-fieldname = 'PRICE'.
gs_fieldcat-do_sum = 'X'. "自动求和
gs_fieldcat-cfieldname = 'CURRENCY'."货币参考(避免不同币种混加)

方式二:通过 Sort 设置小计

" 在 Sort 中设置 subtot = 'X',会自动对该分组求和
gs_sort-fieldname = 'CARRID'.
gs_sort-subtot = 'X'. "按航空公司分组小计

注意:要显示小计行,必须先设置排序,且 subtot = 'X'。求和字段必须是数值类型。


第五部分:事件处理

5.1 Function ALV 的回调事件

Function ALV 通过回调 Form 处理事件:

CALL FUNCTION 'REUSE_ALV_GRID_DISPLAY'
EXPORTING
i_callback_program = sy-repid
i_callback_pf_status_set = 'SET_STATUS' "工具栏
i_callback_user_command = 'USER_COMMAND' "用户命令
i_callback_top_of_page = 'TOP_OF_PAGE' "页眉
i_callback_html_top_of_page = 'HTML_TOP_OF_PAGE' "HTML 页眉
i_callback_html_end_of_list = 'HTML_END_OF_LIST' "HTML 页脚
i_structure_name = 'SFLIGHT'
TABLES
t_outtab = gt_flights.

5.2 USER_COMMAND 用户命令事件

处理用户点击热点、双击行、自定义按钮等操作:

FORM user_command USING pv_ucomm    TYPE sy-ucomm
ps_selfield TYPE slis_selfield.

CASE pv_ucomm.
" 双击事件(默认功能码 &ETA 或 F2)
WHEN '&ETA' OR '&IC1'.
READ TABLE gt_flights INTO gs_flight INDEX ps_selfield-tabindex.
IF sy-subrc = 0.
MESSAGE |你双击了: { gs_flight-carrid }-{ gs_flight-connid }| TYPE 'I'.
ENDIF.

" 自定义按钮事件
WHEN 'BTN_REFRESH'.
PERFORM refresh_data.
ps_selfield-refresh = 'X'. "刷新 ALV

WHEN 'BTN_SAVE'.
PERFORM save_data.

ENDCASE.

" 获取 Grid 对象并刷新(用于可编辑 ALV)
DATA: lo_grid TYPE REF TO cl_gui_alv_grid.
CALL FUNCTION 'GET_GLOBALS_FROM_SLVC_FULLSCR'
IMPORTING
e_grid = lo_grid.
lo_grid->check_changed_data( ). "检查修改数据
ps_selfield-refresh = 'X'.
ENDFORM.

5.3 TOP_OF_PAGE 页眉

FORM top_of_page.
DATA: lt_header TYPE slis_t_listheader,
ls_header TYPE slis_listheader.

" 信息行(带图标)
ls_header-typ = 'S'. "S=信息, H=标题, A=操作
ls_header-key = '报表日期'.
ls_header-info = sy-datum.
APPEND ls_header TO lt_header.

" 标题行
CLEAR ls_header.
ls_header-typ = 'H'.
ls_header-info = '航班信息查询报表'.
APPEND ls_header TO lt_header.

" 普通行
CLEAR ls_header.
ls_header-typ = 'A'.
ls_header-info = '数据来源: SFLIGHT 表'.
APPEND ls_header TO lt_header.

CALL FUNCTION 'REUSE_ALV_COMMENTARY_WRITE'
EXPORTING
it_list_commentary = lt_header.
ENDFORM.

5.4 HOTSPOT 热点事件

在 Fieldcat 中设置 hotspot = 'X',用户点击该列时触发 USER_COMMAND

" 设置热点
gs_fieldcat-fieldname = 'CARRID'.
gs_fieldcat-hotspot = 'X'.

" 在 USER_COMMAND 中处理
FORM user_command USING pv_ucomm TYPE sy-ucomm
ps_selfield TYPE slis_selfield.
IF ps_selfield-fieldname = 'CARRID' AND ps_selfield-value IS NOT INITIAL.
" 跳转到航空公司详情(事务码 SE16 查看)
SET PARAMETER ID 'CAR' FIELD ps_selfield-value.
CALL TRANSACTION 'SAPBC400_FLIGHTS_CAR' AND SKIP FIRST SCREEN.
ENDIF.
ENDFORM.

5.5 DOUBLE_CLICK 双击事件

" 方式一:通过 Layout 的 f2code
gs_layout-f2code = '&ETA'. "双击触发 &ETA 功能码

" 方式二:在 USER_COMMAND 中判断
FORM user_command USING pv_ucomm TYPE sy-ucomm
ps_selfield TYPE slis_selfield.
IF pv_ucomm = '&ETA' OR pv_ucomm = '&IC1'.
" ps_selfield-tabindex = 行号
" ps_selfield-fieldname = 列名
" ps_selfield-value = 单元格值
READ TABLE gt_flights INTO gs_flight INDEX ps_selfield-tabindex.
IF sy-subrc = 0.
" 显示详细信息
CALL FUNCTION 'POPUP_TO_DISPLAY_TEXT'
EXPORTING
titel = '详细信息'
textline1 = |航空公司: { gs_flight-carrid }|
textline2 = |航班号: { gs_flight-connid }|
textline3 = |价格: { gs_flight-price }|.
ENDIF.
ENDIF.
ENDFORM.

第六部分:ALV 可编辑与数据回写

6.1 整列可编辑

在 Fieldcat 中设置 edit = 'X'

LOOP AT gt_fieldcat INTO gs_fieldcat.
CASE gs_fieldcat-fieldname.
WHEN 'PRICE' OR 'SEATSOCC'.
gs_fieldcat-edit = 'X'. "这些列可编辑
WHEN OTHERS.
gs_fieldcat-edit = ''. "其他列只读
ENDCASE.
MODIFY gt_fieldcat FROM gs_fieldcat.
ENDLOOP.

6.2 单元格动态可编辑

通过内表中的样式字段控制每个单元格是否可编辑:

" 1. 定义内表时增加样式字段
TYPES: BEGIN OF ty_flights,
carrid TYPE sflight-carrid,
connid TYPE sflight-connid,
price TYPE sflight-price,
fldate TYPE sflight-fldate,
cellstyles TYPE lvc_t_styl, "单元格样式表
END OF ty_flights.

DATA: gt_flights TYPE STANDARD TABLE OF ty_flights,
gs_flight TYPE ty_flights,
gs_style TYPE lvc_s_styl.

" 2. 填充数据后,设置样式
LOOP AT gt_flights INTO gs_flight.
CLEAR gs_flight-cellstyles.

" 价格字段:所有行可编辑
gs_style-fieldname = 'PRICE'.
gs_style-style = cl_gui_alv_grid=>mc_style_enabled. "可编辑
INSERT gs_style INTO TABLE gs_flight-cellstyles.

" 日期字段:只有航空公司为 'LH' 的行可编辑
gs_style-fieldname = 'FLDATE'.
IF gs_flight-carrid = 'LH'.
gs_style-style = cl_gui_alv_grid=>mc_style_enabled.
ELSE.
gs_style-style = cl_gui_alv_grid=>mc_style_disabled. "不可编辑
ENDIF.
INSERT gs_style INTO TABLE gs_flight-cellstyles.

MODIFY gt_flights FROM gs_flight.
ENDLOOP.

" 3. Layout 中指定样式字段名
gs_layout_lvc-stylefname = 'CELLSTYLES'.

注意lvc_t_styl 是排序表,fieldname 字段必须按字母顺序排列,否则会 DUMP!

6.3 CHECKBOX 复选框

" 方式一:通过 Fieldcat
gs_fieldcat-fieldname = 'SEL'.
gs_fieldcat-checkbox = 'X'.
gs_fieldcat-edit = 'X'.
gs_fieldcat-seltext_l = '选择'.

" 方式二:通过 Layout 的 box_fieldname
gs_layout-box_fieldname = 'SEL'. "内表中增加 SEL TYPE C 字段

6.4 读取修改后的数据并保存

FORM user_command USING pv_ucomm TYPE sy-ucomm
ps_selfield TYPE slis_selfield.

DATA: lo_grid TYPE REF TO cl_gui_alv_grid.

" 获取 Grid 对象
CALL FUNCTION 'GET_GLOBALS_FROM_SLVC_FULLSCR'
IMPORTING
e_grid = lo_grid.

" 将 ALV 中修改的数据同步到内表(关键步骤!)
lo_grid->check_changed_data( ).

CASE pv_ucomm.
WHEN 'SAVE'.
LOOP AT gt_flights INTO gs_flight WHERE sel = 'X'.
" 执行保存逻辑
UPDATE sflight SET price = gs_flight-price
seatsocc = gs_flight-seatsocc
WHERE carrid = gs_flight-carrid
AND connid = gs_flight-connid
AND fldate = gs_flight-fldate.
ENDLOOP.
IF sy-subrc = 0.
COMMIT WORK.
MESSAGE '保存成功' TYPE 'S'.
ENDIF.

" 刷新 ALV
ps_selfield-refresh = 'X'.
lo_grid->refresh_table_display( ).
ENDCASE.
ENDFORM.

第七部分:自定义工具栏

7.1 添加自定义按钮

" 第1步:在 ALV 调用中指定 PF-STATUS
CALL FUNCTION 'REUSE_ALV_GRID_DISPLAY'
EXPORTING
i_callback_pf_status_set = 'SET_STATUS'
i_callback_user_command = 'USER_COMMAND'.

" 第2步:设置 GUI Status
FORM set_status USING pt_extab TYPE slis_t_extab.
SET PF-STATUS 'ZALV_STATUS'. "自定义 GUI Status
ENDFORM.

" 第3步:GUI Status 中添加按钮(SE41 创建)
" 在应用程序工具栏中添加按钮:
" 功能码: BTN_SAVE 图标: ICON_SYSTEM_SAVE 文本: 保存
" 功能码: BTN_PRINT 图标: ICON_PRINT 文本: 打印
" 功能码: BTN_EMAIL 图标: ICON_MAIL 文本: 发邮件

7.2 隐藏标准按钮

FORM set_status USING pt_extab TYPE slis_t_extab.
" 隐藏不需要的标准按钮
DATA: lt_excl TYPE TABLE OF sy-ucomm.

APPEND '&ODN' TO lt_excl. "隐藏升序排序
APPEND '&OUP' TO lt_excl. "隐藏降序排序
APPEND '&SUM' TO lt_excl. "隐藏汇总按钮

SET PF-STATUS 'STANDARD_FULLSCREEN' EXCLUDING lt_excl.
ENDFORM.

7.3 标准按钮功能码速查

功能码 说明
&ETA 双击/详细
&OUP 降序排序
&ODN 升序排序
&SUM 汇总
&RNT_PREV 打印预览
&XXL 导出到 Excel
&ABC ABC 分析
&GRAPH 图形显示
&INFO 信息
&SORT_ASC 升序
&SORT_DSC 降序
&FILTER 过滤
&FIND 查找
&REFRESH 刷新

第八部分:ALV 颜色控制

ALV 支持三种粒度的颜色控制:行颜色、列颜色、单元格颜色。

8.1 行颜色

在内表中增加一个字段存储颜色代码,通过 Layout 的 info_fieldname 指定:

" 1. 定义内表时增加颜色字段
TYPES: BEGIN OF ty_flights,
carrid TYPE sflight-carrid,
connid TYPE sflight-connid,
price TYPE sflight-price,
rowcolor TYPE char4, "行颜色字段
END OF ty_flights.

" 2. 设置颜色值
" 颜色格式: Cxyz
" C = 颜色标识(固定)
" x = 颜色 (0-7): 0=无, 1=灰蓝, 2=正蓝, 3=蓝绿, 4=绿, 5=黄, 6=红, 7=粉
" y = 强化 (0=正常, 1=强化)
" z = 反转 (0=背景色, 1=前景/字体色)
LOOP AT gt_flights INTO gs_flight.
IF gs_flight-price > 1000.
gs_flight-rowcolor = 'C610'. "红色强化前景
ELSEIF gs_flight-price > 500.
gs_flight-rowcolor = 'C510'. "黄色强化前景
ENDIF.
MODIFY gt_flights FROM gs_flight.
ENDLOOP.

" 3. Layout 中指定颜色字段
gs_layout-info_fieldname = 'ROWCOLOR'.

8.2 列颜色

在 Fieldcat 中设置 emphasize 属性:

" 颜色代码同上 Cxyz
gs_fieldcat-fieldname = 'PRICE'.
gs_fieldcat-emphasize = 'C300'. "整列蓝绿色背景

gs_fieldcat-fieldname = 'SEATSOCC'.
gs_fieldcat-emphasize = 'C600'. "整列红色背景

8.3 单元格颜色

在内表中增加单元格颜色字段,通过 Layout 的 coltab_fieldname 指定:

" 1. 定义内表
TYPES: BEGIN OF ty_flights,
carrid TYPE sflight-carrid,
connid TYPE sflight-connid,
price TYPE sflight-price,
cellcolors TYPE lvc_t_scol, "单元格颜色表
END OF ty_flights.

DATA: gs_color TYPE lvc_s_scol.

" 2. 设置单元格颜色
LOOP AT gt_flights INTO gs_flight.
CLEAR gs_flight-cellcolors.

" PRICE 字段颜色
IF gs_flight-price > 1000.
gs_color-fname = 'PRICE'. "字段名
gs_color-color-col = 6. "颜色号 (1-7)
gs_color-color-int = 1. "强化 (0/1)
gs_color-color-inv = 0. "反转 (0/1)
APPEND gs_color TO gs_flight-cellcolors.
ENDIF.

" CARRID 字段颜色
gs_color-fname = 'CARRID'.
gs_color-color-col = 3.
gs_color-color-int = 0.
gs_color-color-inv = 0.
APPEND gs_color TO gs_flight-cellcolors.

MODIFY gt_flights FROM gs_flight.
ENDLOOP.

" 3. Layout 中指定颜色字段
gs_layout_lvc-ctab_fname = 'CELLCOLORS'.

8.4 颜色代码速查表

代码 颜色 代码 颜色
C100 灰蓝(深) C110 灰蓝(浅)
C200 正蓝(深) C210 正蓝(浅)
C300 蓝绿(深) C310 蓝绿(浅)
C400 绿色(深) C410 绿色(浅)
C500 黄色(深) C510 黄色(浅)
C600 红色(深) C610 红色(浅)
C700 粉色(深) C710 粉色(浅)

第九部分:面向对象 ALV(CL_GUI_ALV_GRID)

当 Function ALV 的回调机制无法满足需求时(如需要精确控制事件、嵌入自定义容器等),可以使用 OO ALV。

9.1 基本实现步骤

第1步: 创建屏幕和自定义容器控件
第2步: 创建容器对象 (CL_GUI_CUSTOM_CONTAINER)
第3步: 创建 ALV Grid 对象 (CL_GUI_ALV_GRID)
第4步: 构建 Fieldcat (LVC 格式)
第5步: 调用 SET_TABLE_FOR_FIRST_DISPLAY

9.2 完整示例

REPORT zdemo_oalv_01.

*----------------------------------------------------------------------*
* 类型定义
*----------------------------------------------------------------------*
TYPES: BEGIN OF ty_sflight,
carrid TYPE sflight-carrid,
connid TYPE sflight-connid,
fldate TYPE sflight-fldate,
price TYPE sflight-price,
currency TYPE sflight-currency,
seatsmax TYPE sflight-seatsmax,
seatsocc TYPE sflight-seatsocc,
END OF ty_sflight.

*----------------------------------------------------------------------*
* 全局变量
*----------------------------------------------------------------------*
DATA: gt_flights TYPE STANDARD TABLE OF ty_sflight,
gt_fcat TYPE lvc_t_fcat,
gs_layout TYPE lvc_s_layo.

DATA: go_container TYPE REF TO cl_gui_custom_container,
go_grid TYPE REF TO cl_gui_alv_grid.

*----------------------------------------------------------------------*
* 事件处理类定义
*----------------------------------------------------------------------*
CLASS lcl_event_handler DEFINITION.
PUBLIC SECTION.
" 双击事件
METHODS handle_double_click
FOR EVENT double_click OF cl_gui_alv_grid
IMPORTING e_row e_column es_row_no.

" 数据修改完成事件
METHODS handle_data_changed
FOR EVENT data_changed_finished OF cl_gui_alv_grid
IMPORTING e_modified et_good_cells.

" 工具栏按钮点击事件
METHODS handle_toolbar
FOR EVENT toolbar OF cl_gui_alv_grid
IMPORTING e_object e_interactive.

" 用户命令事件
METHODS handle_user_command
FOR EVENT user_command OF cl_gui_alv_grid
IMPORTING e_ucomm.
ENDCLASS.

*----------------------------------------------------------------------*
* 事件处理类实现
*----------------------------------------------------------------------*
CLASS lcl_event_handler IMPLEMENTATION.

METHOD handle_double_click.
READ TABLE gt_flights INTO DATA(ls_flight) INDEX e_row-index.
IF sy-subrc = 0.
MESSAGE |双击了第{ e_row-index }行, 列:{ e_column-fieldname }, 值:{ ls_flight-carrid }-{ ls_flight-connid }| TYPE 'I'.
ENDIF.
ENDMETHOD.

METHOD handle_data_changed.
" 数据修改后的处理逻辑
MESSAGE |有{ lines( et_good_cells ) }个单元格被修改| TYPE 'S'.
ENDMETHOD.

METHOD handle_toolbar.
" 添加自定义按钮
DATA: ls_button TYPE stb_button.

ls_button-function = 'BTN_SAVE'.
ls_button-icon = icon_system_save.
ls_button-text = '保存'.
ls_button-quickinfo = '保存修改'.
APPEND ls_button TO e_object->mt_toolbar.

CLEAR ls_button.
ls_button-function = 'BTN_REFRESH'.
ls_button-icon = icon_refresh.
ls_button-text = '刷新'.
APPEND ls_button TO e_object->mt_toolbar.

" 添加分隔符
CLEAR ls_button.
ls_button-butn_type = 3. "分隔符
APPEND ls_button TO e_object->mt_toolbar.

" 移除不需要的标准按钮
DELETE e_object->mt_toolbar WHERE function = '&SORT_ASC'.
DELETE e_object->mt_toolbar WHERE function = '&SORT_DSC'.
ENDMETHOD.

METHOD handle_user_command.
CASE e_ucomm.
WHEN 'BTN_SAVE'.
" 同步修改数据
go_grid->check_changed_data( ).
LOOP AT gt_flights INTO DATA(ls_fl).
" 保存逻辑...
ENDLOOP.
COMMIT WORK.
MESSAGE '保存成功' TYPE 'S'.
go_grid->refresh_table_display( ).

WHEN 'BTN_REFRESH'.
PERFORM get_data.
go_grid->refresh_table_display( ).
ENDCASE.
ENDMETHOD.

ENDCLASS.

*----------------------------------------------------------------------*
* 主程序
*----------------------------------------------------------------------*
START-OF-SELECTION.
PERFORM get_data.
CALL SCREEN 100.

*----------------------------------------------------------------------*
* Module: PBO
*----------------------------------------------------------------------*
MODULE status_0100 OUTPUT.
SET PF-STATUS 'MAIN100'.
SET TITLEbar 'T100'.

IF go_container IS NOT BOUND.
" 创建容器
CREATE OBJECT go_container
EXPORTING
container_name = 'CON_ALV'. "屏幕上的自定义控件名

" 创建 ALV Grid
CREATE OBJECT go_grid
EXPORTING
i_parent = go_container.

" 构建 Fieldcat
PERFORM build_fieldcat.

" 设置 Layout
gs_layout-zebra = 'X'.
gs_layout-cwidth_opt = 'X'.
gs_layout-grid_title = '航班信息(OO ALV)'.
gs_layout-sel_mode = 'A'.

" 注册事件处理器
DATA: lo_event TYPE REF TO lcl_event_handler.
CREATE OBJECT lo_event.
SET HANDLER lo_event->handle_double_click FOR go_grid.
SET HANDLER lo_event->handle_data_changed FOR go_grid.
SET HANDLER lo_event->handle_toolbar FOR go_grid.
SET HANDLER lo_event->handle_user_command FOR go_grid.

" 设置可编辑(允许用户修改后触发事件)
go_grid->register_edit_event(
i_event_id = cl_gui_alv_grid=>mc_evt_modified ).

" 显示 ALV
go_grid->set_table_for_first_display(
EXPORTING
is_layout = gs_layout
CHANGING
it_outtab = gt_flights
it_fieldcatalog = gt_fcat ).
ELSE.
go_grid->refresh_table_display( ).
ENDIF.
ENDMODULE.

*----------------------------------------------------------------------*
* Module: PAI
*----------------------------------------------------------------------*
MODULE user_command_0100 INPUT.
CASE sy-ucomm.
WHEN 'BACK' OR 'EXIT' OR 'CANCEL'.
go_grid->free( ).
go_container->free( ).
LEAVE TO SCREEN 0.
ENDCASE.
ENDMODULE.

*----------------------------------------------------------------------*
* Form: 取数
*----------------------------------------------------------------------*
FORM get_data.
REFRESH gt_flights.
SELECT carrid connid fldate price currency seatsmax seatsocc
FROM sflight
INTO TABLE gt_flights
UP TO 200 ROWS.
ENDFORM.

*----------------------------------------------------------------------*
* Form: 构建 Fieldcat
*----------------------------------------------------------------------*
FORM build_fieldcat.
DATA: ls_fcat TYPE lvc_fcat.

CALL FUNCTION 'LVC_FIELDCATALOG_MERGE'
EXPORTING
i_structure_name = 'SFLIGHT'
CHANGING
ct_fieldcat = gt_fcat.

" 调整字段属性
LOOP AT gt_fcat INTO ls_fcat.
CASE ls_fcat-fieldname.
WHEN 'CARRID'.
ls_fcat-coltext = '航空公司'.
ls_fcat-hotspot = 'X'.
ls_fcat-fix_column = 'X'.
WHEN 'CONNID'.
ls_fcat-coltext = '航班号'.
WHEN 'FLDATE'.
ls_fcat-coltext = '日期'.
WHEN 'PRICE'.
ls_fcat-coltext = '价格'.
ls_fcat-do_sum = 'X'.
ls_fcat-edit = 'X'.
ls_fcat-cfieldname = 'CURRENCY'.
WHEN 'CURRENCY'.
ls_fcat-coltext = '货币'.
WHEN 'SEATSMAX'.
ls_fcat-coltext = '最大座位'.
WHEN 'SEATSOCC'.
ls_fcat-coltext = '已占座位'.
ENDCASE.
MODIFY gt_fcat FROM ls_fcat.
ENDLOOP.
ENDFORM.

屏幕设计说明:在 SE51 中创建屏幕 100,添加一个 Custom Control 控件,命名为 CON_ALV


第十部分:SALV 新框架(CL_SALV_TABLE)

SALV 是 SAP NetWeaver 7.02 之后推荐的 ALV 实现方式,代码更简洁,面向对象封装更完善。

10.1 最简 SALV 示例

REPORT zdemo_salv_01.

DATA: gt_flights TYPE STANDARD TABLE OF sflight.

SELECT * FROM sflight INTO TABLE gt_flights UP TO 100 ROWS.

TRY.
cl_salv_table=>factory(
IMPORTING
r_salv_table = DATA(go_salv)
CHANGING
t_table = gt_flights ).

go_salv->display( ).

CATCH cx_salv_msg INTO DATA(lx_msg).
MESSAGE lx_msg->get_text( ) TYPE 'E'.
ENDTRY.

仅 10 行核心代码! 对比 Function ALV 和 OO ALV,SALV 的代码量大幅减少。

10.2 SALV 完整功能配置

REPORT zdemo_salv_02.

DATA: gt_flights TYPE STANDARD TABLE OF sflight.

SELECT * FROM sflight INTO TABLE gt_flights UP TO 200 ROWS.

TRY.
cl_salv_table=>factory(
IMPORTING r_salv_table = DATA(go_salv)
CHANGING t_table = gt_flights ).

" ========== 1. 功能设置 ==========
DATA(lo_funcs) = go_salv->get_functions( ).
lo_funcs->set_all( abap_true ). "启用所有标准功能

" ========== 2. 布局设置 ==========
DATA(lo_layout) = go_salv->get_layout( ).
DATA(ls_key) = VALUE slis_layout_key( report = sy-repid ).
lo_layout->set_key( ls_key ).
lo_layout->set_save_restriction( if_salv_c_layout=>restrict_user_independant ).
lo_layout->set_default( abap_true ).

" ========== 3. 列设置 ==========
DATA(lo_columns) = go_salv->get_columns( ).
lo_columns->set_optimize( abap_true ). "列宽优化

TRY.
DATA(lo_col_carrid) = CAST cl_salv_column_table(
lo_columns->get_column( 'CARRID' ) ).
lo_col_carrid->set_short_text( '航线' ).
lo_col_carrid->set_medium_text( '航空公司' ).
lo_col_carrid->set_long_text( '航空公司代码' ).
lo_col_carrid->set_key( abap_true ). "设为主键
CATCH cx_salv_not_found.
ENDTRY.

TRY.
DATA(lo_col_price) = CAST cl_salv_column_table(
lo_columns->get_column( 'PRICE' ) ).
lo_col_price->set_short_text( '价格' ).
lo_col_price->set_do_sum( abap_true ). "求和
CATCH cx_salv_not_found.
ENDTRY.

" 隐藏列
TRY.
lo_columns->get_column( 'MANDT' )->set_visible( abap_false ).
CATCH cx_salv_not_found.
ENDTRY.

" ========== 4. 显示设置 ==========
DATA(lo_display) = go_salv->get_display_settings( ).
lo_display->set_striped_pattern( abap_true ). "斑马纹
lo_display->set_list_header( '航班信息查询' ). "标题

" ========== 5. 选择模式 ==========
DATA(lo_selections) = go_salv->get_selections( ).
lo_selections->set_selection_mode( if_salv_c_selection_mode=>row_column ).

" ========== 6. 事件处理 ==========
DATA(lo_events) = go_salv->get_event( ).
DATA(lo_event_h) = NEW lcl_salv_handler( ).
SET HANDLER lo_event_h->on_double_click FOR lo_events.

" ========== 7. 排序 ==========
DATA(lo_sorts) = go_salv->get_sorts( ).
TRY.
lo_sorts->add_sort(
columnname = 'CARRID'
subtotal = abap_true ).
CATCH cx_salv_not_found cx_salv_existing cx_salv_data_error.
ENDTRY.

" ========== 8. 聚合(求和) ==========
DATA(lo_aggs) = go_salv->get_aggregations( ).
TRY.
lo_aggs->add_aggregation(
columnname = 'PRICE'
aggregation = if_salv_c_aggregation=>total ).
CATCH cx_salv_not_found cx_salv_data_error cx_salv_existing.
ENDTRY.

" ========== 9. 页眉 ==========
DATA(lo_header) = NEW cl_salv_form_layout_grid( ).
lo_header->create_label(
row = 1 column = 1 )->set_text( '航班信息查询报表' ).
lo_header->create_label(
row = 2 column = 1 )->set_text( |报表日期: { sy-datum }| ).
go_salv->set_top_of_list( lo_header ).

" 显示
go_salv->display( ).

CATCH cx_salv_msg INTO DATA(lx_msg).
MESSAGE lx_msg->get_text( ) TYPE 'E'.
ENDTRY.

10.3 SALV 事件处理类

CLASS lcl_salv_handler DEFINITION.
PUBLIC SECTION.
METHODS on_double_click
FOR EVENT if_salv_events_actions_table~double_click
OF cl_salv_events_table
IMPORTING row column.
ENDCLASS.

CLASS lcl_salv_handler IMPLEMENTATION.
METHOD on_double_click.
DATA: lt_flights TYPE STANDARD TABLE OF sflight.

" 从 SALV 对象获取数据
" 注意:row 是行号, column 是列名
MESSAGE |双击: 行{ row }, 列{ column }| TYPE 'S'.
ENDMETHOD.
ENDCLASS.

10.4 SALV 显示模式

SALV 支持三种显示模式:

" 表格模式(默认,最常用)
go_salv->set_screen_status(
report = sy-repid
pfstatus = 'SALV_STANDARD'
set_functions = go_salv->c_functions_all ).

" 全屏幕列表模式
go_salv->display( ).

" 嵌入容器模式(需要自己创建容器)
cl_salv_table=>factory(
EXPORTING
r_container = go_container "CL_GUI_CUSTOM_CONTAINER
IMPORTING
r_salv_table = go_salv
CHANGING
t_table = gt_flights ).
go_salv->display( ).

10.5 层级 SALV(Hierarchical)

用于显示主从表关联数据(如订单头+订单行项目):

REPORT zdemo_salv_hierseq.

" 定义主表和子表类型
TYPES: BEGIN OF ty_vbak,
vbeln TYPE vbak-vbeln,
erdat TYPE vbak-erdat,
ernam TYPE vbak-ernam,
netwr TYPE vbak-netwr,
END OF ty_vbak.

TYPES: BEGIN OF ty_vbap,
vbeln TYPE vbap-vbeln,
posnr TYPE vbap-posnr,
matnr TYPE vbap-matnr,
kwmeng TYPE vbap-kwmeng,
netwr TYPE vbap-netwr,
END OF ty_vbap.

DATA: lt_vbak TYPE STANDARD TABLE OF ty_vbak,
lt_vbap TYPE STANDARD TABLE OF ty_vbap.

" 取数
SELECT vbeln erdat ernam netwr FROM vbak INTO TABLE lt_vbak UP TO 20 ROWS.
IF lt_vbap IS NOT INITIAL.
SELECT vbeln posnr matnr kwmeng netwr FROM vbap
INTO TABLE lt_vbap
FOR ALL ENTRIES IN lt_vbak
WHERE vbeln = lt_vbak-vbeln.
ENDIF.

" 关联绑定
DATA: lt_binding TYPE salv_t_hierseq_binding.
APPEND VALUE #( master = 'VBELN' slave = 'VBELN' ) TO lt_binding.

TRY.
cl_salv_hierseq_table=>factory(
EXPORTING
t_binding_level1_level2 = lt_binding
IMPORTING
r_hierseq = DATA(go_hier)
CHANGING
t_table_level1 = lt_vbak
t_table_level2 = lt_vbap ).

" 设置列
DATA(lo_cols_l1) = go_hier->get_columns( 1 ).
lo_cols_l1->set_optimize( abap_true ).

DATA(lo_cols_l2) = go_hier->get_columns( 2 ).
lo_cols_l2->set_optimize( abap_true ).

" 功能
go_hier->get_functions( )->set_all( abap_true ).

" 显示
go_hier->display( ).

CATCH cx_salv_data_error cx_salv_not_found INTO DATA(lx).
MESSAGE lx->get_text( ) TYPE 'E'.
ENDTRY.

第十一部分:多表格 ALV(Block List)

在一个屏幕中显示多个独立表格:

REPORT zdemo_alv_block.

DATA: gt_flights TYPE STANDARD TABLE OF sflight,
gt_bookings TYPE STANDARD TABLE OF sbook,
gt_fieldcat1 TYPE slis_t_fieldcat_alv,
gt_fieldcat2 TYPE slis_t_fieldcat_alv.

" 取数
SELECT * FROM sflight INTO TABLE gt_flights UP TO 20 ROWS.
SELECT * FROM sbook INTO TABLE gt_bookings UP TO 30 ROWS.

" 构建两个表的 Fieldcat
CALL FUNCTION 'REUSE_ALV_FIELDCATALOG_MERGE'
EXPORTING
i_structure_name = 'SFLIGHT'
CHANGING
ct_fieldcat = gt_fieldcat1.

CALL FUNCTION 'REUSE_ALV_FIELDCATALOG_MERGE'
EXPORTING
i_structure_name = 'SBOOK'
CHANGING
ct_fieldcat = gt_fieldcat2.

" 第1步:初始化 Block List
CALL FUNCTION 'REUSE_ALV_BLOCK_LIST_INIT'
EXPORTING
i_callback_program = sy-repid
i_callback_user_command = 'USER_COMMAND'.

" 第2步:追加第一个表格
CALL FUNCTION 'REUSE_ALV_BLOCK_LIST_APPEND'
EXPORTING
is_layout = VALUE slis_layout_alv( colwidth_optimize = 'X' )
it_fieldcat = gt_fieldcat1
i_tabname = 'GT_FLIGHTS'
it_events = VALUE slis_t_event( )
it_sort = VALUE slis_t_sortinfo_alv( )
i_text = '航班信息'
TABLES
t_outtab = gt_flights
EXCEPTIONS
program_error = 1
OTHERS = 2.

" 第3步:追加第二个表格
CALL FUNCTION 'REUSE_ALV_BLOCK_LIST_APPEND'
EXPORTING
is_layout = VALUE slis_layout_alv( colwidth_optimize = 'X' )
it_fieldcat = gt_fieldcat2
i_tabname = 'GT_BOOKINGS'
i_text = '订票信息'
TABLES
t_outtab = gt_bookings.

" 第4步:显示
CALL FUNCTION 'REUSE_ALV_BLOCK_LIST_DISPLAY'
EXCEPTIONS
program_error = 1
OTHERS = 2.

第十二部分:ALV Tree

ALV Tree 以树形结构显示数据,适合 BOM 展开、组织架构等层级数据。

REPORT zdemo_alv_tree.

DATA: gt_spfli TYPE STANDARD TABLE OF spfli,
gt_sflight TYPE STANDARD TABLE OF sflight,
go_tree TYPE REF TO cl_gui_alv_tree_simple,
go_cont TYPE REF TO cl_gui_custom_container,
gt_fcat TYPE lvc_t_fcat,
gt_sort TYPE lvc_t_sort.

SELECT * FROM spfli INTO TABLE gt_spfli UP TO 20 ROWS.
SELECT * FROM sflight INTO TABLE gt_sflight UP TO 100 ROWS.

" 创建容器和 Tree 对象
CREATE OBJECT go_cont
EXPORTING container_name = 'CON_TREE'.

CREATE OBJECT go_tree
EXPORTING
i_parent = go_cont
i_node_selection_mode = cl_gui_column_tree=>node_sel_mode_single.

" 构建 Fieldcat
CALL FUNCTION 'LVC_FIELDCATALOG_MERGE'
EXPORTING i_structure_name = 'SFLIGHT'
CHANGING ct_fieldcat = gt_fcat.

" 设置排序(定义层级)
APPEND VALUE lvc_sort( fieldname = 'CARRID' up = 'X' ) TO gt_sort.
APPEND VALUE lvc_sort( fieldname = 'CONNID' up = 'X' ) TO gt_sort.

" 设置显示
go_tree->set_table_for_first_display(
EXPORTING
is_layout = VALUE lvc_s_layo( tree_autoexpand = 'X' )
CHANGING
it_outtab = gt_sflight
it_fieldcatalog = gt_fcat
it_sort = gt_sort ).

go_tree->column_optimize( ).
go_tree->update_calcs( ).
go_tree->frontend_update( ).

第十三部分:实战技巧

13.1 上传 Excel 数据到 ALV

REPORT zdemo_excel_upload.

TYPES: BEGIN OF ty_data,
bukrs TYPE bukrs,
ebeln TYPE ebeln,
ebelp TYPE ebelp,
matnr TYPE matnr,
menge TYPE menge_d,
status TYPE char4, "状态图标
message TYPE char100, "消息
END OF ty_data.

DATA: gt_excel TYPE TABLE OF alsmex_tabline,
gt_output TYPE STANDARD TABLE OF ty_data,
gs_output TYPE ty_data.

PARAMETERS: p_file TYPE rlgrap-filename OBLIGATORY.

" 文件选择
AT SELECTION-SCREEN ON VALUE-REQUEST FOR p_file.
CALL FUNCTION 'F4_FILENAME'
IMPORTING
file_name = p_file.

START-OF-SELECTION.

" 调用 FM 将 Excel 转为内表
CALL FUNCTION 'ALSM_EXCEL_TO_INTERNAL_TABLE'
EXPORTING
filename = p_file
i_begin_col = 1
i_begin_row = 2 "跳过标题行
i_end_col = 5
i_end_row = 65535
TABLES
intern = gt_excel
EXCEPTIONS
inconsistent_parameters = 1
upload_ole = 2
OTHERS = 3.

" 将 Excel 行列数据转为结构化内表
SORT gt_excel BY row col.
LOOP AT gt_excel INTO DATA(ls_excel).
CASE ls_excel-col.
WHEN 1. gs_output-bukrs = ls_excel-value.
WHEN 2. gs_output-ebeln = ls_excel-value.
WHEN 3. gs_output-ebelp = ls_excel-value.
WHEN 4. gs_output-matnr = ls_excel-value.
WHEN 5. gs_output-menge = ls_excel-value.
ENDCASE.

AT END OF row.
" 数据校验
IF gs_output-bukrs IS INITIAL OR gs_output-ebeln IS INITIAL.
gs_output-status = icon_led_red.
gs_output-message = '公司代码和采购订单不能为空'.
ELSE.
gs_output-status = icon_led_green.
gs_output-message = '校验通过'.
ENDIF.
APPEND gs_output TO gt_output.
CLEAR gs_output.
ENDAT.
ENDLOOP.

" ALV 显示
DATA: gt_fieldcat TYPE slis_t_fieldcat_alv.
CALL FUNCTION 'REUSE_ALV_FIELDCATALOG_MERGE'
EXPORTING
i_structure_name = 'ZEXCEL_UPLOAD_S'
CHANGING
ct_fieldcat = gt_fieldcat.

CALL FUNCTION 'REUSE_ALV_GRID_DISPLAY'
EXPORTING
i_structure_name = 'ZEXCEL_UPLOAD_S'
is_layout = VALUE slis_layout_alv(
colwidth_optimize = 'X'
info_fieldname = '' )
it_fieldcat = gt_fieldcat
TABLES
t_outtab = gt_output.

13.2 ALV 导出 Excel(增强导出)

" ALV 自带导出功能(工具栏按钮 &XXL)
" 如需自定义导出,可用以下方法:

DATA: lo_grid TYPE REF TO cl_gui_alv_grid.

" 获取 Grid 对象
CALL FUNCTION 'GET_GLOBALS_FROM_SLVC_FULLSCR'
IMPORTING e_grid = lo_grid.

" 方法一:调用内置导出
lo_grid->export_to_spreadsheet(
EXPORTING
filename = 'C:\temp\alv_export.xls'
filetype = 'XLS'
codepage = '4110' ). "UTF-8

13.3 动态 Fieldcat(不依赖 DDIC 结构)

当内表是动态构建的,无法使用 REUSE_ALV_FIELDCATALOG_MERGE 时,需要手动构建:

REPORT zdemo_dynamic_fieldcat.

DATA: lo_struct TYPE REF TO cl_abap_structdescr,
lo_table TYPE REF TO cl_abap_tabledescr,
lt_comp TYPE cl_abap_structdescr=>component_table,
ls_comp LIKE LINE OF lt_comp,
lo_data TYPE REF TO data,
lo_line TYPE REF TO data.

" 动态构建结构
ls_comp-name = 'CARRID'.
ls_comp-type ?= cl_abap_typedescr=>describe_by_name( 'S_CARR_ID' ).
APPEND ls_comp TO lt_comp.

ls_comp-name = 'CONNID'.
ls_comp-type ?= cl_abap_typedescr=>describe_by_name( 'S_CONN_ID' ).
APPEND ls_comp TO lt_comp.

ls_comp-name = 'PRICE'.
ls_comp-type ?= cl_abap_typedescr=>describe_by_name( 'S_PRICE' ).
APPEND ls_comp TO lt_comp.

lo_struct = cl_abap_structdescr=>create( lt_comp ).
lo_table = cl_abap_tabledescr=>create( lo_struct ).
CREATE DATA lo_data TYPE HANDLE lo_table.
CREATE DATA lo_line TYPE HANDLE lo_struct.

FIELD-SYMBOLS: <lt_data> TYPE STANDARD TABLE,
<ls_data> TYPE any.
ASSIGN lo_data->* TO <lt_data>.
ASSIGN lo_line->* TO <ls_data>.

" 取数(动态)
SELECT carrid connid price FROM sflight INTO CORRESPONDING FIELDS OF TABLE <lt_data> UP TO 50 ROWS.

" 动态构建 Fieldcat
DATA: gt_fcat TYPE lvc_t_fcat,
gs_fcat TYPE lvc_fcat.

LOOP AT lt_comp INTO ls_comp.
CLEAR gs_fcat.
gs_fcat-fieldname = ls_comp-name.
gs_fcat-datatype = ls_comp-type->type_kind.
gs_fcat-intlen = ls_comp-type->length.
CASE ls_comp-name.
WHEN 'CARRID'. gs_fcat-coltext = '航空公司'.
WHEN 'CONNID'. gs_fcat-coltext = '航班号'.
WHEN 'PRICE'. gs_fcat-coltext = '价格'.
ENDCASE.
APPEND gs_fcat TO gt_fcat.
ENDLOOP.

" 显示
DATA: lo_grid TYPE REF TO cl_gui_alv_grid,
lo_cont TYPE REF TO cl_gui_custom_container.

CREATE OBJECT lo_cont EXPORTING container_name = 'CON_ALV'.
CREATE OBJECT lo_grid EXPORTING i_parent = lo_cont.

lo_grid->set_table_for_first_display(
EXPORTING is_layout = VALUE lvc_s_layo( cwidth_opt = 'X' zebra = 'X' )
CHANGING it_outtab = <lt_data> it_fieldcatalog = gt_fcat ).

13.4 ALV 性能优化技巧

" 1. SELECT 时指定字段,避免 SELECT *
SELECT carrid connid fldate price FROM sflight
INTO TABLE gt_flights
WHERE carrid IN s_carrid.

" 2. 使用 FOR ALL ENTRIES 替代嵌套 SELECT
IF gt_flights IS NOT INITIAL.
SELECT vbeln posnr matnr FROM vbap
INTO TABLE gt_vbap
FOR ALL ENTRIES IN gt_flights
WHERE vbeln = gt_flights-vbeln.
ENDIF.

" 3. 大数据量时使用分页或限制行数
SELECT ... INTO TABLE gt_data UP TO p_max ROWS.

" 4. 避免在 LOOP 中调用 FM/BAPI
" 错误示范:
LOOP AT gt_data INTO gs_data.
CALL FUNCTION 'SOMETHING' ... "每行调一次,性能极差
ENDLOOP.

" 正确示范:批量处理
CALL FUNCTION 'SOMETHING_BATCH' "一次传入整批数据
TABLES it_data = gt_data.

" 5. ALV 显示前做 SORT
SORT gt_flights BY carrid connid.

" 6. 大量数据时用 REUSE_ALV_GRID_DISPLAY_LVC 代替 REUSE_ALV_GRID_DISPLAY
" LVC 版本性能更好,功能更全

" 7. 使用 REFRESH_TABLE_DISPLAY 增量刷新
" 避免每次都重新 set_table_for_first_display

13.5 ALV 红绿灯状态

在 ALV 中显示红绿灯图标,直观表示数据状态:

" 1. 定义内表时增加图标字段
TYPES: BEGIN OF ty_data,
ebeln TYPE ekko-ebeln,
status TYPE char4, "图标字段
message TYPE char100,
END OF ty_data.

" 2. 设置图标值
" 图标常量定义在 TYPE-POOL ICON 中
gs_data-status = icon_green_light. "绿灯: @5B@
gs_data-status = icon_yellow_light. "黄灯: @5D@
gs_data-status = icon_red_light. "红灯: @5A@

" 3. Fieldcat 中设置 icon 属性
gs_fieldcat-fieldname = 'STATUS'.
gs_fieldcat-icon = 'X'. "声明该字段是图标
gs_fieldcat-seltext_l = '状态'.

" 4. 常用图标常量
" icon_green_light = '@5B@' 绿灯(成功)
" icon_yellow_light = '@5D@' 黄灯(警告)
" icon_red_light = '@5A@' 红灯(错误)
" icon_led_green = '@5B@'
" icon_led_yellow = '@5D@'
" icon_led_red = '@5A@'
" icon_check = '@01@' 勾选
" icon_delete = '@11@' 删除
" icon_change = '@0Z@' 修改
" icon_display = '@10@' 显示
" icon_create = '@17@' 创建

13.6 金额和数量字段的正确显示

金额和数量字段需要关联参考字段才能正确显示小数点和单位:

" 金额字段
gs_fieldcat-fieldname = 'NETWR'.
gs_fieldcat-datatype = 'CURR'. "数据类型为货币
gs_fieldcat-cfieldname = 'WAERS'. "关联货币字段

" 数量字段
gs_fieldcat-fieldname = 'MENGE'.
gs_fieldcat-datatype = 'QUAN'. "数据类型为数量
gs_fieldcat-qfieldname = 'MEINS'. "关联单位字段

" LVC 格式
gs_fcat-fieldname = 'NETWR'.
gs_fcat-cfieldname = 'WAERS'.
gs_fcat-datatype = 'CURR'.

第十四部分:常用函数/方法速查

14.1 Function ALV 函数

函数 说明
REUSE_ALV_GRID_DISPLAY ALV Grid 显示(最常用)
REUSE_ALV_GRID_DISPLAY_LVC ALV Grid LVC 版(推荐)
REUSE_ALV_LIST_DISPLAY ALV 列表显示
REUSE_ALV_BLOCK_LIST_INIT 多表格初始化
REUSE_ALV_BLOCK_LIST_APPEND 多表格追加
REUSE_ALV_BLOCK_LIST_DISPLAY 多表格显示
REUSE_ALV_HIERSEQ_LIST_DISPLAY 层级列表显示
REUSE_ALV_FIELDCATALOG_MERGE 自动生成 Fieldcat
LVC_FIELDCATALOG_MERGE LVC 格式 Fieldcat
REUSE_ALV_COMMENTARY_WRITE 写页眉注释
REUSE_ALV_VARIANT_F4 布局变式选择对话框
GET_GLOBALS_FROM_SLVC_FULLSCR 获取 Grid 对象

14.2 OO ALV 类

说明
CL_GUI_ALV_GRID ALV Grid 控件类
CL_GUI_CUSTOM_CONTAINER 自定义容器
CL_GUI_CONTAINER 容器基类
CL_GUI_SPLITTER_CONTAINER 分割容器
CL_SALV_TABLE SALV 表格(推荐)
CL_SALV_HIERSEQ_TABLE SALV 层级表格
CL_SALV_TREE SALV 树形
CL_SALV_FUNCTIONS SALV 功能设置
CL_SALV_COLUMNS SALV 列设置
CL_SALV_COLUMN_TABLE SALV 单列设置
CL_SALV_DISPLAY_SETTINGS SALV 显示设置
CL_SALV_SELECTIONS SALV 选择设置
CL_SALV_SORTS SALV 排序设置
CL_SALV_AGGREGATIONS SALV 聚合设置
CL_SALV_EVENTS_TABLE SALV 事件
CL_SALV_FORM_LAYOUT_GRID SALV 页眉布局

14.3 常用类型

类型 说明 用于
SLIS_T_FIELDCAT_ALV Fieldcat 表 Function ALV
SLIS_FIELDCAT_ALV Fieldcat 行 Function ALV
SLIS_LAYOUT_ALV Layout 结构 Function ALV
SLIS_T_SORTINFO_ALV Sort 表 Function ALV
SLIS_T_EVENT 事件表 Function ALV
LVC_T_FCAT Fieldcat 表 OO ALV
LVC_S_FCAT Fieldcat 行 OO ALV
LVC_S_LAYO Layout 结构 OO ALV
LVC_T_SORT Sort 表 OO ALV
LVC_T_STYL 单元格样式表 动态可编辑
LVC_T_SCOL 单元格颜色表 单元格颜色

第十五部分:常见问题与注意事项

15.1 常见错误

错误 原因 解决
Fieldcat 无列标题 内表用 DATA 定义,FM 无法识别 TYPES 定义结构
金额显示异常 未设置 cfieldname 关联货币参考字段
求和不生效 字段类型非数值 确保 datatype 正确
编辑后数据丢失 未调用 check_changed_data 在 USER_COMMAND 中先调用
单元格样式 DUMP lvc_t_styl 字段未排序 按 fieldname 字母序排列
列标题显示为字段名 DDIC 中无文本描述 手动设置 seltext_l
REUSE_ALV_GRID_DISPLAY 不触发事件 未设置 i_callback_program 传入 sy-repid
OO ALV 屏幕空白 容器名与屏幕控件名不一致 检查 container_name

15.2 最佳实践

  1. 选择哪种方式?

    • 简单报表 → REUSE_ALV_GRID_DISPLAY + i_structure_name
    • 需要精细控制 → OO ALV (CL_GUI_ALV_GRID)
    • 新开发首选 → SALV (CL_SALV_TABLE)
  2. 内表定义方式

    • 必须用 TYPES 定义结构,再用 DATA ... TYPE TABLE OF ...
    • 不要用 DATA: BEGIN OF ... OCCURS 0(已过时)
  3. Fieldcat 构建

    • 推荐自动构建 + 手动调整混合方式
    • 常用字段:coltext/seltext_loutputleneditdo_sumhotspot
  4. 大数据量

    • 使用 UP TO n ROWS 限制
    • 考虑分页或搜索条件限制
    • 优先用 REUSE_ALV_GRID_DISPLAY_LVC
  5. 代码组织

    • 将 Fieldcat 构建、数据获取、ALV 显示分到不同 FORM/方法
    • 使用常量定义功能码、颜色代码等

附录:ALV 发展历程

版本 年份 特性
R/3 3.x 1990s WRITE 列表,无 ALV
R/3 4.6 ~2000 引入 ALV(Function 方式)
NW 04 ~2004 引入 CL_GUI_ALV_GRID(OO 方式)
NW 7.02 ~2009 引入 CL_SALV_TABLE(SALV 方式)
S/4HANA 2015+ SALV 增强,支持 CDS 视图 ALV
ABAP Cloud 2020+ 新 ALV API,面向 RAP/Fiori

推荐路径:新项目统一使用 SALV(CL_SALV_TABLE),维护老代码时了解 Function ALV 和 OO ALV 即可。