目录
SAP中关于ALV展示的函数都不支持深层内表的展示,之前在做接口日志的时候,由于很多接口需要将字段设计成深层结构,看到过一些公司前辈编写的日志框架,关于接口参数的展示,或使用ALV TREE,或使用ALV,但都有一个问题,不支持深层结构字段的显示,但往往接口参数中,经常会有参数类型是深层结构的,所以一直想解决这个问题,我们国内做项目绝大部分关于报表展示都用的是REUSE_ALV_GRID_DISPLAY_LVC,所以我改造了一个基于该函数展示为前提的DEEP ALV框架,实现过程参考了Github上一位大佬的思路,不过他使用的是cl_salv_table这个类去实现的,且需要借助额外的协助程序来完善功能,在实际使用上还有一些其他问题,于是我重新写了一份新的框架,最终效果如下:
测试程序ALV展示使用的内表对应的行类型如下,其中包括一个表类型字段_ITEMS,和一个深层结构字段_INFO。
实现思路:
1.以下是代码实现部分:
SE24创建自开发类:
属性部分如下:
方法列表如下:
DISPLAY_DEEP_STRUCTURE:展示ALV公用方法
METHOD display_deep_structure.
FIELD-SYMBOLS:
<fs_new_data> TYPE STANDARD TABLE.
DATA:
lt_fieldcat TYPE lvc_t_fcat.
* 将展示数据使用堆栈管理
_update_display_stack( table = i_table
operation = mc_stack_go ).
* 绑定全局变量
IF i_callback_pf_status_set IS NOT INITIAL.
mv_callback_pf_status_set = i_callback_pf_status_set.
ENDIF.
IF i_callback_user_command IS NOT INITIAL.
mv_callback_user_command = i_callback_user_command.
ENDIF.
IF i_custom_fcat IS NOT INITIAL.
mt_custom_fcat = i_custom_fcat.
ENDIF.
CLEAR mt_components.
mt_components = _get_line_component( table = i_table ).
* 判断是否包含表类型或者结构类型字段
LOOP AT mt_components TRANSPORTING NO FIELDS
WHERE type->type_kind = cl_abap_elemdescr=>typekind_table
OR type->type_kind = cl_abap_elemdescr=>typekind_struct2.
EXIT.
ENDLOOP.
* 如果存在表类型字段
IF sy-subrc = 0.
DATA(lo_new_data_ref) = _get_type_handle( i_table ).
ASSIGN lo_new_data_ref->* TO <fs_new_data>.
_fill_condensed_data( EXPORTING table = i_table
CHANGING condensed_data = <fs_new_data> ).
ELSE.
ASSIGN i_table TO <fs_new_data>.
ENDIF.
* 构建fieldcat内表
lt_fieldcat = _build_fieldcat( table = i_table ).
* 展示ALV
me->_display_condensed_alv(
EXPORTING
condensed_data = <fs_new_data>
pf_status_set = mv_callback_pf_status_set
user_command = mv_callback_user_command
custom_fieldcat_lvc = mt_custom_fcat
CHANGING
fieldcat_lvc = lt_fieldcat
layout_lvc = i_custom_layo
).
ENDMETHOD.
_UPDATE_DISPLAY_STACK:更新管理堆栈
METHOD _update_display_stack.
DATA:
lo_curr_disp TYPE REF TO data.
FIELD-SYMBOLS: <table> TYPE ANY TABLE.
CASE operation.
WHEN mc_stack_back.
DELETE mo_display_stack INDEX 1.
WHEN mc_stack_go.
IF table IS NOT SUPPLIED.
RAISE no_data.
ELSE.
* 获取类型对象
DATA(lo_handle) = CAST cl_abap_tabledescr( cl_abap_tabledescr=>describe_by_data( table ) ).
* 基于类型对象创建数据
CREATE DATA lo_curr_disp TYPE HANDLE lo_handle.
* 指针分配
ASSIGN lo_curr_disp->* TO <table>.
LOOP AT table ASSIGNING FIELD-SYMBOL(<line>).
INSERT INITIAL LINE INTO TABLE <table> ASSIGNING FIELD-SYMBOL(<new_line>).
<new_line> = CORRESPONDING #( <line> ).
ENDLOOP.
* 将数据保存至堆栈最上层
INSERT lo_curr_disp INTO mo_display_stack INDEX 1.
ENDIF.
ENDCASE.
ENDMETHOD.
_GET_LINE_COMPONENT:获取行类型组件
METHOD _get_line_component.
component_table = CAST cl_abap_structdescr(
CAST cl_abap_tabledescr(
cl_abap_tabledescr=>describe_by_data( table )
)->get_table_line_type( )
)->get_components( ).
ENDMETHOD.
_GET_TYPE_HANDLE:构建新扁平类型初始内表
METHOD _get_type_handle.
* 创建新的扁平类型对象,将表类型或者深层结构字段替换为CHAR类型
DATA(lo_new_tab) = cl_abap_tabledescr=>create(
p_line_type = cl_abap_structdescr=>create(
VALUE #( FOR wa IN mt_components
( SWITCH #(
wa-type->type_kind
* 表类型
WHEN cl_abap_elemdescr=>typekind_table
THEN VALUE #( name = wa-name type = cl_abap_elemdescr=>get_c( p_length = 70 ) )
* 深层结构
WHEN cl_abap_elemdescr=>typekind_struct2
THEN VALUE #( name = wa-name type = cl_abap_elemdescr=>get_c( p_length = 70 ) )
ELSE VALUE #( name = wa-name type = wa-type ) )
)
)
)
p_table_kind = cl_abap_tabledescr=>tablekind_std
p_unique = abap_false
).
CREATE DATA new_table TYPE HANDLE lo_new_tab.
ENDMETHOD.
_FILL_CONDENSED_DATA:mapping填充新的扁平类型内表
METHOD _fill_condensed_data.
FIELD-SYMBOLS:
<fs_table> TYPE ANY TABLE.
LOOP AT table ASSIGNING FIELD-SYMBOL(<fs_line>).
INSERT INITIAL LINE INTO TABLE condensed_data ASSIGNING FIELD-SYMBOL(<fs_new_line>).
LOOP AT mt_components ASSIGNING FIELD-SYMBOL(<fs_component>).
CASE <fs_component>-type->type_kind.
* 表类型
WHEN cl_abap_elemdescr=>typekind_table.
ASSIGN COMPONENT <fs_component>-name OF STRUCTURE <fs_line> TO <fs_table>.
IF <fs_table> IS ASSIGNED.
ASSIGN COMPONENT <fs_component>-name OF STRUCTURE <fs_new_line> TO FIELD-SYMBOL(<fs_tab_desc>).
IF <fs_tab_desc> IS ASSIGNED.
<fs_tab_desc> = |{ icon_list }Items[ { lines( <fs_table> ) } ]|.
UNASSIGN:
<fs_tab_desc>,
<fs_table>.
ENDIF.
ENDIF.
* 结构类型
WHEN cl_abap_elemdescr=>typekind_struct2.
ASSIGN COMPONENT <fs_component>-name OF STRUCTURE <fs_line> TO FIELD-SYMBOL(<fs_struc>).
IF <fs_struc> IS ASSIGNED.
ASSIGN COMPONENT <fs_component>-name OF STRUCTURE <fs_new_line> TO <fs_tab_desc>.
IF <fs_tab_desc> IS ASSIGNED.
<fs_tab_desc> = icon_structure.
UNASSIGN:
<fs_tab_desc>,
<fs_struc>.
ENDIF.
ENDIF.
* 其他类型
WHEN OTHERS.
ASSIGN COMPONENT <fs_component>-name OF STRUCTURE <fs_line> TO FIELD-SYMBOL(<fs_value>).
IF <fs_value> IS ASSIGNED.
ASSIGN COMPONENT <fs_component>-name OF STRUCTURE <fs_new_line> TO FIELD-SYMBOL(<fs_value_new>).
IF <fs_value_new> IS ASSIGNED.
<fs_value_new> = <fs_value>.
UNASSIGN:
<fs_value>,
<fs_value_new>.
ENDIF.
ENDIF.
ENDCASE.
ENDLOOP.
ENDLOOP.
ENDMETHOD.
_BUILD_FIELDCAT:自动构建fieldcat(没有使用函数自动获取是因为函数的返回并不满足我的需求)
METHOD _build_fieldcat.
DATA:
lv_struc_name TYPE tabname,
ls_fieldcat TYPE lvc_s_fcat.
* 获取行类型结构名
DATA(lv_structure) = CAST cl_abap_tabledescr( cl_abap_tabledescr=>describe_by_data( table ) )->get_table_line_type( )->get_relative_name( ).
lv_struc_name = lv_structure.
* 获取fieldcat信息
SELECT a~tabname,
a~fieldname,
a~position,
a~rollname,
a~inttype,
a~datatype,
a~leng,
a~domname,
a~comptype,
b~reftable,
b~reffield,
b~reptext,
b~scrtext_s,
b~scrtext_m,
b~scrtext_l,
c~lowercase,
c~convexit,
d~ddtext AS d_ddtext,
e~ddtext AS t_ddtext,
f~ddtext AS s_ddtext
FROM dd03l AS a
LEFT OUTER JOIN dd03vt AS b
ON a~tabname = b~tabname
AND a~fieldname = b~fieldname
AND a~position = b~position
AND a~comptype = b~comptype
AND b~ddlanguage = @sy-langu
LEFT OUTER JOIN dd04l AS c
ON a~rollname = c~rollname
AND a~as4local = c~as4local
LEFT OUTER JOIN dd03t AS d
ON a~tabname = d~tabname
AND a~as4local = d~as4local
AND a~fieldname = d~fieldname
AND d~ddlanguage = @sy-langu
LEFT OUTER JOIN dd40t AS e
ON a~rollname = e~typename
AND e~ddlanguage = @sy-langu
LEFT OUTER JOIN dd02t AS f
ON a~rollname = f~tabname
AND f~ddlanguage = @sy-langu
WHERE a~tabname = @lv_struc_name
AND a~as4local = 'A'
AND a~depth = ''
INTO TABLE @DATA(lt_fieldinfo).
SORT lt_fieldinfo BY position ASCENDING.
LOOP AT lt_fieldinfo INTO DATA(ls_fieldinfo).
ls_fieldcat-col_pos = ls_fieldinfo-position.
ls_fieldcat-fieldname = ls_fieldinfo-fieldname.
CASE ls_fieldinfo-datatype.
* 数量类型
WHEN 'QUAN'.
ls_fieldcat-qfieldname = ls_fieldinfo-reffield.
* 金额类型
WHEN 'CURR'.
ls_fieldcat-cfieldname = ls_fieldinfo-reffield.
WHEN OTHERS.
ENDCASE.
ls_fieldcat-convexit = ls_fieldinfo-convexit.
ls_fieldcat-datatype = ls_fieldinfo-datatype.
ls_fieldcat-inttype = ls_fieldinfo-inttype.
ls_fieldcat-intlen = ls_fieldinfo-leng.
ls_fieldcat-lowercase = ls_fieldinfo-lowercase.
ls_fieldcat-reptext = ls_fieldinfo-reptext.
CASE ls_fieldinfo-comptype.
* 内置类型
WHEN ''.
ls_fieldcat-reptext = ls_fieldinfo-d_ddtext.
* 表类型
WHEN 'L'.
ls_fieldcat-hotspot = 'X'.
ls_fieldcat-reptext = ls_fieldinfo-t_ddtext.
ls_fieldcat-just = 'L'.
* 结构
WHEN 'S'.
ls_fieldcat-hotspot = 'X'.
ls_fieldcat-reptext = ls_fieldinfo-s_ddtext.
ls_fieldcat-just = 'L'.
WHEN OTHERS.
ENDCASE.
ls_fieldcat-domname = ls_fieldinfo-domname.
ls_fieldcat-ref_table = lv_struc_name.
ls_fieldcat-dd_outlen = ls_fieldinfo-leng.
ls_fieldcat-scrtext_s = ls_fieldinfo-scrtext_s.
ls_fieldcat-scrtext_m = ls_fieldinfo-scrtext_m.
ls_fieldcat-scrtext_l = ls_fieldinfo-scrtext_l.
APPEND ls_fieldcat TO fieldcat.
CLEAR ls_fieldcat.
ENDLOOP.
ENDMETHOD.
_DISPLAY_CONDENSED_ALV:最终展示处理
METHOD _display_condensed_alv.
DATA:
lv_tabix TYPE i,
lv_index TYPE i.
DATA:
lt_event_exit TYPE slis_t_event_exit,
ls_event_exit TYPE slis_event_exit.
FIELD-SYMBOLS <fs_alv> TYPE STANDARD TABLE.
* 设置默认布局控制
IF layout_lvc IS INITIAL.
layout_lvc-zebra = 'X'. "颜色间隔
layout_lvc-sel_mode = 'D'. "选择模式
layout_lvc-cwidth_opt = 'A'. "列宽自适应
ENDIF.
* 如果存在自定义字段设置,将其与标准获取的字段设置合并
IF custom_fieldcat_lvc IS NOT INITIAL.
LOOP AT custom_fieldcat_lvc INTO DATA(ls_cus_fcat).
READ TABLE fieldcat_lvc ASSIGNING FIELD-SYMBOL(<fs_fieldcat>)
WITH KEY fieldname = ls_cus_fcat-fieldname
ref_table = ls_cus_fcat-ref_table.
IF sy-subrc = 0.
DO.
ADD 1 TO lv_index.
ASSIGN COMPONENT lv_index OF STRUCTURE ls_cus_fcat TO FIELD-SYMBOL(<fs_cus_fcat>).
IF <fs_cus_fcat> IS ASSIGNED.
ASSIGN COMPONENT lv_index OF STRUCTURE <fs_fieldcat> TO FIELD-SYMBOL(<fs_fcat>).
IF <fs_cus_fcat> <> <fs_fcat> AND <fs_cus_fcat> IS NOT INITIAL.
<fs_fcat> = <fs_cus_fcat>.
ENDIF.
ELSE.
EXIT.
ENDIF.
UNASSIGN:
<fs_cus_fcat>,
<fs_fcat>.
ENDDO.
CLEAR lv_index.
ENDIF.
ENDLOOP.
ENDIF.
ASSIGN condensed_data TO <fs_alv>.
* 使标准按钮->"返回",可以触发用户自定义USER_COMMAND回调函数
ls_event_exit-ucomm = '&F03'.
ls_event_exit-after = 'X'.
APPEND ls_event_exit TO lt_event_exit.
* 展示ALV
CALL FUNCTION 'REUSE_ALV_GRID_DISPLAY_LVC'
EXPORTING
i_callback_program = sy-cprog
i_callback_pf_status_set = pf_status_set
i_callback_user_command = user_command
is_layout_lvc = layout_lvc
it_fieldcat_lvc = fieldcat_lvc
i_save = 'A'
it_event_exit = lt_event_exit
TABLES
t_outtab = <fs_alv>
EXCEPTIONS
program_error = 1
OTHERS = 2.
ENDMETHOD.
HOTSPOT_CLICK:处理热点双击事件
METHOD hotspot_click.
FIELD-SYMBOLS:
<fs_current_display> TYPE INDEX TABLE,
<fs_table> TYPE STANDARD TABLE,
<fs_struc> TYPE any.
DATA:
lo_current_display TYPE REF TO data,
lv_obj_type TYPE char1,
lo_data TYPE REF TO data.
* 获取当前界面展示扁平内表对应的原始内表(深层内表)
READ TABLE mo_display_stack INTO lo_current_display INDEX 1.
ASSIGN lo_current_display->* TO <fs_current_display>.
READ TABLE <fs_current_display> INDEX i_selfield-tabindex ASSIGNING FIELD-SYMBOL(<fs_current_row>).
IF <fs_current_row> IS ASSIGNED.
* 获取点击单元格数据内容
ASSIGN COMPONENT i_selfield-fieldname OF STRUCTURE <fs_current_row> TO FIELD-SYMBOL(<fs_deep_field>).
* 获取点击单元格数据类型
lv_obj_type = cl_abap_tabledescr=>describe_by_data( <fs_deep_field> )->type_kind.
CASE lv_obj_type.
* 表类型
WHEN cl_abap_elemdescr=>typekind_table.
ASSIGN <fs_deep_field> TO <fs_table>.
* 深层结构
WHEN cl_abap_elemdescr=>typekind_struct2.
CREATE DATA lo_data LIKE TABLE OF <fs_deep_field>.
ASSIGN lo_data->* TO <fs_table>.
APPEND <fs_deep_field> TO <fs_table>.
WHEN OTHERS.
ENDCASE.
IF <fs_table> IS ASSIGNED.
IF <fs_table> IS NOT INITIAL.
display_deep_structure( i_table = <fs_table>
i_callback_user_command = mv_callback_user_command ).
ELSE.
MESSAGE 'No Data!' TYPE 'S'.
ENDIF.
ENDIF.
ENDIF.
ENDMETHOD.
BACK_CLICK:捕获标准返回按钮事件
METHOD back_click.
_update_display_stack( operation = mc_stack_back ).
ENDMETHOD.
至此,框架代码完成。
2.编写程序进行测试:
主程序部分:
*&---------------------------------------------------------------------*
*& Report ZDEEP_ALV_13065
*&---------------------------------------------------------------------*
*&
*&---------------------------------------------------------------------*
REPORT zdeep_alv_13065.
TABLES:vbak.
TYPES:
ty_tab_alv TYPE ztdeep_head,
ty_alv TYPE zsdeep_head,
ty_tab_item TYPE ztdeep_item,
ty_item TYPE zsdeep_item.
DATA:
gt_alv TYPE ty_tab_alv,
go_deep_alv TYPE REF TO zdeep_alv.
SELECT-OPTIONS:s_vbeln FOR vbak-vbeln.
START-OF-SELECTION.
PERFORM frm_get_data.
PERFORM frm_show_deep_alv.
取数部分:
*&---------------------------------------------------------------------*
*& Form FRM_GET_DATA
*&---------------------------------------------------------------------*
*& 获取数据
*&---------------------------------------------------------------------*
FORM frm_get_data .
DATA:
ls_item TYPE ty_item,
lt_item TYPE ty_tab_item,
ls_head TYPE ty_alv,
ls_head_info TYPE zsdeep_head_info,
ls_return TYPE bapiret2.
SELECT a~vbeln,
a~ernam,
a~audat,
a~auart,
a~waerk,
a~vkorg,
a~vtweg,
b~posnr,
b~matnr,
b~charg,
b~matkl,
b~zmeng,
b~zieme,
b~netwr,
b~waerk AS item_waerk,
a~kunnr
INTO TABLE @DATA(lt_salesorder)
FROM vbak AS a
INNER JOIN vbap AS b
ON a~vbeln = b~vbeln
WHERE a~vbeln IN @s_vbeln
AND a~auart = 'ZOR1'.
SORT lt_salesorder BY vbeln ASCENDING
posnr ASCENDING.
LOOP AT lt_salesorder INTO DATA(ls_salesorder)
GROUP BY ( vbeln = ls_salesorder-vbeln ) ASCENDING
REFERENCE INTO DATA(lo_salesorder).
LOOP AT GROUP lo_salesorder ASSIGNING FIELD-SYMBOL(<fs_salesorder>).
ls_item-posnr = <fs_salesorder>-posnr.
ls_item-matnr = <fs_salesorder>-matnr.
ls_item-charg = <fs_salesorder>-charg.
ls_item-matkl = <fs_salesorder>-matkl.
ls_item-zmeng = <fs_salesorder>-zmeng.
ls_item-zieme = <fs_salesorder>-zieme.
ls_item-netwr = <fs_salesorder>-netwr.
ls_item-waerk = <fs_salesorder>-item_waerk.
ls_item-ztest = 'Test..'.
APPEND ls_item TO lt_item.
CLEAR ls_item.
ENDLOOP.
ls_head-_items = lt_item.
ls_head-vbeln = <fs_salesorder>-vbeln.
ls_head-ernam = <fs_salesorder>-ernam.
ls_head-audat = <fs_salesorder>-audat.
ls_head-auart = <fs_salesorder>-auart.
ls_head-waerk = <fs_salesorder>-waerk.
ls_head-vkorg = <fs_salesorder>-vkorg.
ls_head-vtweg = <fs_salesorder>-vtweg.
ls_head_info-uname = sy-uname.
ls_head_info-utext = sy-datum.
ls_return-type = 'S'.
ls_return-message = 'this is a test message'.
APPEND ls_return TO ls_head_info-_message.
ls_head-_info = ls_head_info.
ls_head-kunnr = <fs_salesorder>-kunnr.
APPEND ls_head TO gt_alv.
CLEAR:
ls_head,
lt_item,
ls_item,
ls_head_info.
ENDLOOP.
ENDFORM.
调用框架进行ALV展示:
*&---------------------------------------------------------------------*
*& Form FRM_SHOW_DEEP_ALV
*&---------------------------------------------------------------------*
*& 展示ALV
*&---------------------------------------------------------------------*
FORM frm_show_deep_alv .
DATA:
ls_layout TYPE lvc_s_layo,
lv_struc_name TYPE tabname,
lt_fieldcat TYPE lvc_t_fcat,
ls_fieldcat TYPE lvc_s_fcat.
ls_fieldcat-fieldname = 'VBELN'. "字段名
ls_fieldcat-ref_table = 'ZSDEEP_HEAD'. "参考表类型
ls_fieldcat-reptext = 'My订单'.
ls_fieldcat-scrtext_s = 'My订单'.
ls_fieldcat-scrtext_m = 'My订单'.
ls_fieldcat-scrtext_l = 'My订单'.
APPEND ls_fieldcat TO lt_fieldcat.
ls_fieldcat-fieldname = '_MESSAGE'. "字段名
ls_fieldcat-ref_table = 'ZSDEEP_HEAD_INFO'. "参考表类型
ls_fieldcat-reptext = 'My消息'.
ls_fieldcat-scrtext_s = 'My消息'.
ls_fieldcat-scrtext_m = 'My消息'.
ls_fieldcat-scrtext_l = 'My消息'.
APPEND ls_fieldcat TO lt_fieldcat.
CREATE OBJECT go_deep_alv.
* SELECT * INTO TABLE @DATA(lt_t001) FROM t001.
go_deep_alv->display_deep_structure(
i_table = gt_alv
* i_table = lt_t001
i_callback_pf_status_set = 'FRM_STATUS_SET'
i_callback_user_command = 'FRM_USER_COMMAND'
* i_custom_fcat = lt_fieldcat
).
ENDFORM.
i_table:必传参数,可以是深层结构内表,也可以是普通扁平类型内表。
i_callback_pf_status_set:选填参数,不传则使用默认标准状态栏。
i_callback_user_command:选填参数,当传入内表为纵深结构时,则需要传入参数指定处理子例程,需要调用框架方法释放堆栈内存;当传入内表为普通扁平结构时,如果没有自定义按钮逻辑,则不需要传入。
i_custom_fcat:选填参数,不传则在框架内默认,传了则在框架内会自动与标准生成的fieldcat信息合并覆盖。
i_custom_layo:选填参数,同fieldcat。
回调函数部分:
*&---------------------------------------------------------------------*
*& Form FRM_STATUS_SET
*&---------------------------------------------------------------------*
*& GUI状态栏设置
*&---------------------------------------------------------------------*
*& --> RT_EXTAB 排除按钮
*&---------------------------------------------------------------------*
FORM frm_status_set USING rt_extab TYPE slis_t_extab.
SET PF-STATUS 'STANDARD'." EXCLUDING RT_EXTAB.
ENDFORM.
*&---------------------------------------------------------------------*
*& Form FRM_USER_COMMAND
*&---------------------------------------------------------------------*
*& 用户按钮事件响应
*&---------------------------------------------------------------------*
*& --> R_UCOMM 事件码
*& --> RS_SELFIELD 操作数据字段信息
*&---------------------------------------------------------------------*
FORM frm_user_command USING r_ucomm LIKE sy-ucomm
rs_selfield TYPE slis_selfield.
CASE r_ucomm.
WHEN '&IC1'.
go_deep_alv->hotspot_click( i_selfield = rs_selfield ).
WHEN '&F03'.
go_deep_alv->back_click( ).
WHEN OTHERS.
ENDCASE.
ENDFORM.
传入深层结构内表时,其中user_command事件中需要对双击事件和返回按钮调用对应的框架方法,来完成界面跳转和堆栈管理,调用完之后,可以进行自定义逻辑的处理。
不使用自定义fieldcat和status效果:
使用自定义status和fieldcat的效果:
普通扁平结构内表的效果:
以上。
框架完整代码如下,如果大家有更好的方式也可以评论。
CLASS zdeep_alv DEFINITION
PUBLIC
FINAL
CREATE PUBLIC .
PUBLIC SECTION.
METHODS constructor .
METHODS display_deep_structure
IMPORTING
VALUE(i_table) TYPE ANY TABLE
VALUE(i_callback_user_command) TYPE slis_formname OPTIONAL
VALUE(i_callback_pf_status_set) TYPE slis_formname OPTIONAL
VALUE(i_custom_fcat) TYPE lvc_t_fcat OPTIONAL
VALUE(i_custom_layo) TYPE lvc_s_layo OPTIONAL .
METHODS hotspot_click
IMPORTING
VALUE(i_selfield) TYPE slis_selfield .
METHODS back_click .
PROTECTED SECTION.
PRIVATE SECTION.
DATA mv_callback_pf_status_set TYPE slis_formname .
DATA mv_callback_user_command TYPE slis_formname .
DATA:
mo_display_stack TYPE STANDARD TABLE OF REF TO data .
CONSTANTS mc_stack_go TYPE char1 VALUE '1' ##NO_TEXT.
CONSTANTS mc_stack_back TYPE char1 VALUE '2' ##NO_TEXT.
DATA mt_components TYPE abap_component_tab .
DATA mt_custom_fcat TYPE lvc_t_fcat .
METHODS _update_display_stack
IMPORTING
!table TYPE ANY TABLE OPTIONAL
!operation TYPE char1
EXCEPTIONS
no_data .
METHODS _get_type_handle
IMPORTING
!table TYPE ANY TABLE
RETURNING
VALUE(new_table) TYPE REF TO data .
METHODS _fill_condensed_data
IMPORTING
VALUE(table) TYPE ANY TABLE
CHANGING
!condensed_data TYPE ANY TABLE .
METHODS _display_condensed_alv
IMPORTING
VALUE(condensed_data) TYPE ANY TABLE
!pf_status_set TYPE slis_formname OPTIONAL
!user_command TYPE slis_formname OPTIONAL
!custom_fieldcat_lvc TYPE lvc_t_fcat OPTIONAL
CHANGING
!fieldcat_lvc TYPE lvc_t_fcat OPTIONAL
!layout_lvc TYPE lvc_s_layo OPTIONAL .
METHODS _get_line_component
IMPORTING
VALUE(table) TYPE ANY TABLE
RETURNING
VALUE(component_table) TYPE abap_component_tab .
METHODS _build_fieldcat
IMPORTING
!table TYPE ANY TABLE
RETURNING
VALUE(fieldcat) TYPE lvc_t_fcat .
ENDCLASS.
CLASS ZDEEP_ALV IMPLEMENTATION.
* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method ZDEEP_ALV->BACK_CLICK
* +-------------------------------------------------------------------------------------------------+
* +--------------------------------------------------------------------------------------</SIGNATURE>
METHOD back_click.
_update_display_stack( operation = mc_stack_back ).
ENDMETHOD.
* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method ZDEEP_ALV->CONSTRUCTOR
* +-------------------------------------------------------------------------------------------------+
* +--------------------------------------------------------------------------------------</SIGNATURE>
METHOD constructor.
ENDMETHOD.
* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method ZDEEP_ALV->DISPLAY_DEEP_STRUCTURE
* +-------------------------------------------------------------------------------------------------+
* | [--->] I_TABLE TYPE ANY TABLE
* | [--->] I_CALLBACK_USER_COMMAND TYPE SLIS_FORMNAME
* | [--->] I_CALLBACK_PF_STATUS_SET TYPE SLIS_FORMNAME(optional)
* | [--->] I_CUSTOM_FCAT TYPE LVC_T_FCAT(optional)
* | [--->] I_CUSTOM_LAYO TYPE LVC_S_LAYO(optional)
* +--------------------------------------------------------------------------------------</SIGNATURE>
METHOD display_deep_structure.
FIELD-SYMBOLS:
<fs_new_data> TYPE STANDARD TABLE.
DATA:
lt_fieldcat TYPE lvc_t_fcat.
* 将展示数据使用堆栈管理
_update_display_stack( table = i_table
operation = mc_stack_go ).
* 绑定全局变量
IF i_callback_pf_status_set IS NOT INITIAL.
mv_callback_pf_status_set = i_callback_pf_status_set.
ENDIF.
IF i_callback_user_command IS NOT INITIAL.
mv_callback_user_command = i_callback_user_command.
ENDIF.
IF i_custom_fcat IS NOT INITIAL.
mt_custom_fcat = i_custom_fcat.
ENDIF.
CLEAR mt_components.
mt_components = _get_line_component( table = i_table ).
* 判断是否包含表类型或者结构类型字段
LOOP AT mt_components TRANSPORTING NO FIELDS
WHERE type->type_kind = cl_abap_elemdescr=>typekind_table
OR type->type_kind = cl_abap_elemdescr=>typekind_struct2.
EXIT.
ENDLOOP.
* 如果存在表类型字段
IF sy-subrc = 0.
* 构建新的扁平类型的内表
DATA(lo_new_data_ref) = _get_type_handle( i_table ).
ASSIGN lo_new_data_ref->* TO <fs_new_data>.
* 填充新的扁平类型的内表
_fill_condensed_data( EXPORTING table = i_table
CHANGING condensed_data = <fs_new_data> ).
ELSE.
ASSIGN i_table TO <fs_new_data>.
ENDIF.
* 构建fieldcat内表
lt_fieldcat = _build_fieldcat( table = i_table ).
* 展示ALV
me->_display_condensed_alv(
EXPORTING
condensed_data = <fs_new_data>
pf_status_set = mv_callback_pf_status_set
user_command = mv_callback_user_command
custom_fieldcat_lvc = mt_custom_fcat
CHANGING
fieldcat_lvc = lt_fieldcat
layout_lvc = i_custom_layo
).
ENDMETHOD.
* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method ZDEEP_ALV->HOTSPOT_CLICK
* +-------------------------------------------------------------------------------------------------+
* | [--->] I_SELFIELD TYPE SLIS_SELFIELD
* +--------------------------------------------------------------------------------------</SIGNATURE>
METHOD hotspot_click.
FIELD-SYMBOLS:
<fs_current_display> TYPE INDEX TABLE,
<fs_table> TYPE STANDARD TABLE,
<fs_struc> TYPE any.
DATA:
lo_current_display TYPE REF TO data,
lv_obj_type TYPE char1,
lo_data TYPE REF TO data.
* 获取当前界面展示扁平内表对应的原始内表(深层内表)
READ TABLE mo_display_stack INTO lo_current_display INDEX 1.
ASSIGN lo_current_display->* TO <fs_current_display>.
READ TABLE <fs_current_display> INDEX i_selfield-tabindex ASSIGNING FIELD-SYMBOL(<fs_current_row>).
IF <fs_current_row> IS ASSIGNED.
* 获取点击单元格数据内容
ASSIGN COMPONENT i_selfield-fieldname OF STRUCTURE <fs_current_row> TO FIELD-SYMBOL(<fs_deep_field>).
* 获取点击单元格数据类型
lv_obj_type = cl_abap_tabledescr=>describe_by_data( <fs_deep_field> )->type_kind.
CASE lv_obj_type.
* 表类型
WHEN cl_abap_elemdescr=>typekind_table.
ASSIGN <fs_deep_field> TO <fs_table>.
* 深层结构
WHEN cl_abap_elemdescr=>typekind_struct2.
CREATE DATA lo_data LIKE TABLE OF <fs_deep_field>.
ASSIGN lo_data->* TO <fs_table>.
APPEND <fs_deep_field> TO <fs_table>.
WHEN OTHERS.
ENDCASE.
IF <fs_table> IS ASSIGNED.
IF <fs_table> IS NOT INITIAL.
display_deep_structure( i_table = <fs_table>
i_callback_user_command = mv_callback_user_command ).
ELSE.
MESSAGE 'No Data!' TYPE 'S'.
ENDIF.
ENDIF.
ENDIF.
ENDMETHOD.
* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Private Method ZDEEP_ALV->_BUILD_FIELDCAT
* +-------------------------------------------------------------------------------------------------+
* | [--->] TABLE TYPE ANY TABLE
* | [<-()] FIELDCAT TYPE LVC_T_FCAT
* +--------------------------------------------------------------------------------------</SIGNATURE>
METHOD _build_fieldcat.
DATA:
lv_struc_name TYPE tabname,
ls_fieldcat TYPE lvc_s_fcat.
* 获取行类型结构名
DATA(lv_structure) = CAST cl_abap_tabledescr( cl_abap_tabledescr=>describe_by_data( table ) )->get_table_line_type( )->get_relative_name( ).
lv_struc_name = lv_structure.
* 获取fieldcat信息
SELECT a~tabname,
a~fieldname,
a~position,
a~rollname,
a~inttype,
a~datatype,
a~leng,
a~domname,
a~comptype,
b~reftable,
b~reffield,
b~reptext,
b~scrtext_s,
b~scrtext_m,
b~scrtext_l,
c~lowercase,
c~convexit,
d~ddtext AS d_ddtext,
e~ddtext AS t_ddtext,
f~ddtext AS s_ddtext
FROM dd03l AS a
LEFT OUTER JOIN dd03vt AS b
ON a~tabname = b~tabname
AND a~fieldname = b~fieldname
AND a~position = b~position
AND a~comptype = b~comptype
AND b~ddlanguage = @sy-langu
LEFT OUTER JOIN dd04l AS c
ON a~rollname = c~rollname
AND a~as4local = c~as4local
LEFT OUTER JOIN dd03t AS d
ON a~tabname = d~tabname
AND a~as4local = d~as4local
AND a~fieldname = d~fieldname
AND d~ddlanguage = @sy-langu
LEFT OUTER JOIN dd40t AS e
ON a~rollname = e~typename
AND e~ddlanguage = @sy-langu
LEFT OUTER JOIN dd02t AS f
ON a~rollname = f~tabname
AND f~ddlanguage = @sy-langu
WHERE a~tabname = @lv_struc_name
AND a~as4local = 'A'
AND a~depth = ''
INTO TABLE @DATA(lt_fieldinfo).
SORT lt_fieldinfo BY position ASCENDING.
LOOP AT lt_fieldinfo INTO DATA(ls_fieldinfo).
ls_fieldcat-col_pos = ls_fieldinfo-position.
ls_fieldcat-fieldname = ls_fieldinfo-fieldname.
CASE ls_fieldinfo-datatype.
* 数量类型
WHEN 'QUAN'.
ls_fieldcat-qfieldname = ls_fieldinfo-reffield.
* 金额类型
WHEN 'CURR'.
ls_fieldcat-cfieldname = ls_fieldinfo-reffield.
WHEN OTHERS.
ENDCASE.
ls_fieldcat-convexit = ls_fieldinfo-convexit.
ls_fieldcat-datatype = ls_fieldinfo-datatype.
ls_fieldcat-inttype = ls_fieldinfo-inttype.
ls_fieldcat-intlen = ls_fieldinfo-leng.
ls_fieldcat-lowercase = ls_fieldinfo-lowercase.
ls_fieldcat-reptext = ls_fieldinfo-reptext.
CASE ls_fieldinfo-comptype.
* 内置类型
WHEN ''.
ls_fieldcat-reptext = ls_fieldinfo-d_ddtext.
* 表类型
WHEN 'L'.
ls_fieldcat-hotspot = 'X'.
ls_fieldcat-reptext = ls_fieldinfo-t_ddtext.
ls_fieldcat-just = 'L'.
* 结构
WHEN 'S'.
ls_fieldcat-hotspot = 'X'.
ls_fieldcat-reptext = ls_fieldinfo-s_ddtext.
ls_fieldcat-just = 'L'.
WHEN OTHERS.
ENDCASE.
ls_fieldcat-domname = ls_fieldinfo-domname.
ls_fieldcat-ref_table = lv_struc_name.
ls_fieldcat-dd_outlen = ls_fieldinfo-leng.
ls_fieldcat-scrtext_s = ls_fieldinfo-scrtext_s.
ls_fieldcat-scrtext_m = ls_fieldinfo-scrtext_m.
ls_fieldcat-scrtext_l = ls_fieldinfo-scrtext_l.
APPEND ls_fieldcat TO fieldcat.
CLEAR ls_fieldcat.
ENDLOOP.
ENDMETHOD.
* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Private Method ZDEEP_ALV->_DISPLAY_CONDENSED_ALV
* +-------------------------------------------------------------------------------------------------+
* | [--->] CONDENSED_DATA TYPE ANY TABLE
* | [--->] PF_STATUS_SET TYPE SLIS_FORMNAME(optional)
* | [--->] USER_COMMAND TYPE SLIS_FORMNAME(optional)
* | [--->] CUSTOM_FIELDCAT_LVC TYPE LVC_T_FCAT(optional)
* | [<-->] FIELDCAT_LVC TYPE LVC_T_FCAT(optional)
* | [<-->] LAYOUT_LVC TYPE LVC_S_LAYO(optional)
* +--------------------------------------------------------------------------------------</SIGNATURE>
METHOD _display_condensed_alv.
DATA:
lv_tabix TYPE i,
lv_index TYPE i.
DATA:
lt_event_exit TYPE slis_t_event_exit,
ls_event_exit TYPE slis_event_exit.
FIELD-SYMBOLS <fs_alv> TYPE STANDARD TABLE.
* 设置默认布局控制
IF layout_lvc IS INITIAL.
layout_lvc-zebra = 'X'. "颜色间隔
layout_lvc-sel_mode = 'D'. "选择模式
layout_lvc-cwidth_opt = 'A'. "列宽自适应
ENDIF.
* 如果存在自定义字段设置,将其与标准获取的字段设置合并
IF custom_fieldcat_lvc IS NOT INITIAL.
LOOP AT custom_fieldcat_lvc INTO DATA(ls_cus_fcat).
READ TABLE fieldcat_lvc ASSIGNING FIELD-SYMBOL(<fs_fieldcat>)
WITH KEY fieldname = ls_cus_fcat-fieldname
ref_table = ls_cus_fcat-ref_table.
IF sy-subrc = 0.
DO.
ADD 1 TO lv_index.
ASSIGN COMPONENT lv_index OF STRUCTURE ls_cus_fcat TO FIELD-SYMBOL(<fs_cus_fcat>).
IF <fs_cus_fcat> IS ASSIGNED.
ASSIGN COMPONENT lv_index OF STRUCTURE <fs_fieldcat> TO FIELD-SYMBOL(<fs_fcat>).
IF <fs_cus_fcat> <> <fs_fcat> AND <fs_cus_fcat> IS NOT INITIAL.
<fs_fcat> = <fs_cus_fcat>.
ENDIF.
ELSE.
EXIT.
ENDIF.
UNASSIGN:
<fs_cus_fcat>,
<fs_fcat>.
ENDDO.
CLEAR lv_index.
ENDIF.
ENDLOOP.
ENDIF.
ASSIGN condensed_data TO <fs_alv>.
* 使标准按钮->"返回",可以触发用户自定义USER_COMMAND回调函数
ls_event_exit-ucomm = '&F03'.
ls_event_exit-after = 'X'.
APPEND ls_event_exit TO lt_event_exit.
* 展示ALV
CALL FUNCTION 'REUSE_ALV_GRID_DISPLAY_LVC'
EXPORTING
i_callback_program = sy-cprog
i_callback_pf_status_set = pf_status_set
i_callback_user_command = user_command
is_layout_lvc = layout_lvc
it_fieldcat_lvc = fieldcat_lvc
i_save = 'A'
it_event_exit = lt_event_exit
TABLES
t_outtab = <fs_alv>
EXCEPTIONS
program_error = 1
OTHERS = 2.
ENDMETHOD.
* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Private Method ZDEEP_ALV->_FILL_CONDENSED_DATA
* +-------------------------------------------------------------------------------------------------+
* | [--->] TABLE TYPE ANY TABLE
* | [<-->] CONDENSED_DATA TYPE ANY TABLE
* +--------------------------------------------------------------------------------------</SIGNATURE>
METHOD _fill_condensed_data.
FIELD-SYMBOLS:
<fs_table> TYPE ANY TABLE.
LOOP AT table ASSIGNING FIELD-SYMBOL(<fs_line>).
INSERT INITIAL LINE INTO TABLE condensed_data ASSIGNING FIELD-SYMBOL(<fs_new_line>).
LOOP AT mt_components ASSIGNING FIELD-SYMBOL(<fs_component>).
CASE <fs_component>-type->type_kind.
* 表类型
WHEN cl_abap_elemdescr=>typekind_table.
ASSIGN COMPONENT <fs_component>-name OF STRUCTURE <fs_line> TO <fs_table>.
IF <fs_table> IS ASSIGNED.
ASSIGN COMPONENT <fs_component>-name OF STRUCTURE <fs_new_line> TO FIELD-SYMBOL(<fs_tab_desc>).
IF <fs_tab_desc> IS ASSIGNED.
<fs_tab_desc> = |{ icon_list }Items[ { lines( <fs_table> ) } ]|.
UNASSIGN:
<fs_tab_desc>,
<fs_table>.
ENDIF.
ENDIF.
* 结构类型
WHEN cl_abap_elemdescr=>typekind_struct2.
ASSIGN COMPONENT <fs_component>-name OF STRUCTURE <fs_line> TO FIELD-SYMBOL(<fs_struc>).
IF <fs_struc> IS ASSIGNED.
ASSIGN COMPONENT <fs_component>-name OF STRUCTURE <fs_new_line> TO <fs_tab_desc>.
IF <fs_tab_desc> IS ASSIGNED.
<fs_tab_desc> = icon_structure.
UNASSIGN:
<fs_tab_desc>,
<fs_struc>.
ENDIF.
ENDIF.
* 其他类型
WHEN OTHERS.
ASSIGN COMPONENT <fs_component>-name OF STRUCTURE <fs_line> TO FIELD-SYMBOL(<fs_value>).
IF <fs_value> IS ASSIGNED.
ASSIGN COMPONENT <fs_component>-name OF STRUCTURE <fs_new_line> TO FIELD-SYMBOL(<fs_value_new>).
IF <fs_value_new> IS ASSIGNED.
<fs_value_new> = <fs_value>.
UNASSIGN:
<fs_value>,
<fs_value_new>.
ENDIF.
ENDIF.
ENDCASE.
ENDLOOP.
ENDLOOP.
ENDMETHOD.
* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Private Method ZDEEP_ALV->_GET_LINE_COMPONENT
* +-------------------------------------------------------------------------------------------------+
* | [--->] TABLE TYPE ANY TABLE
* | [<-()] COMPONENT_TABLE TYPE ABAP_COMPONENT_TAB
* +--------------------------------------------------------------------------------------</SIGNATURE>
METHOD _get_line_component.
component_table = CAST cl_abap_structdescr(
CAST cl_abap_tabledescr(
cl_abap_tabledescr=>describe_by_data( table )
)->get_table_line_type( )
)->get_components( ).
ENDMETHOD.
* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Private Method ZDEEP_ALV->_GET_TYPE_HANDLE
* +-------------------------------------------------------------------------------------------------+
* | [--->] TABLE TYPE ANY TABLE
* | [<-()] NEW_TABLE TYPE REF TO DATA
* +--------------------------------------------------------------------------------------</SIGNATURE>
METHOD _get_type_handle.
* 创建新的扁平类型对象,将表类型或者深层结构字段替换为CHAR类型
DATA(lo_new_tab) = cl_abap_tabledescr=>create(
p_line_type = cl_abap_structdescr=>create(
VALUE #( FOR wa IN mt_components
( SWITCH #(
wa-type->type_kind
* 表类型
WHEN cl_abap_elemdescr=>typekind_table
THEN VALUE #( name = wa-name type = cl_abap_elemdescr=>get_c( p_length = 70 ) )
* 深层结构
WHEN cl_abap_elemdescr=>typekind_struct2
THEN VALUE #( name = wa-name type = cl_abap_elemdescr=>get_c( p_length = 70 ) )
ELSE VALUE #( name = wa-name type = wa-type ) )
)
)
)
p_table_kind = cl_abap_tabledescr=>tablekind_std
p_unique = abap_false
).
CREATE DATA new_table TYPE HANDLE lo_new_tab.
ENDMETHOD.
* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Private Method ZDEEP_ALV->_UPDATE_DISPLAY_STACK
* +-------------------------------------------------------------------------------------------------+
* | [--->] TABLE TYPE ANY TABLE(optional)
* | [--->] OPERATION TYPE CHAR1
* | [EXC!] NO_DATA
* +--------------------------------------------------------------------------------------</SIGNATURE>
METHOD _update_display_stack.
DATA:
lo_curr_disp TYPE REF TO data.
FIELD-SYMBOLS: <table> TYPE ANY TABLE.
CASE operation.
WHEN mc_stack_back.
DELETE mo_display_stack INDEX 1.
WHEN mc_stack_go.
IF table IS NOT SUPPLIED.
RAISE no_data.
ELSE.
* 获取类型对象
DATA(lo_handle) = CAST cl_abap_tabledescr( cl_abap_tabledescr=>describe_by_data( table ) ).
* 基于类型对象创建数据
CREATE DATA lo_curr_disp TYPE HANDLE lo_handle.
* 指针分配
ASSIGN lo_curr_disp->* TO <table>.
LOOP AT table ASSIGNING FIELD-SYMBOL(<line>).
INSERT INITIAL LINE INTO TABLE <table> ASSIGNING FIELD-SYMBOL(<new_line>).
<new_line> = CORRESPONDING #( <line> ).
ENDLOOP.
* 将数据保存至堆栈最上层
INSERT lo_curr_disp INTO mo_display_stack INDEX 1.
ENDIF.
ENDCASE.
ENDMETHOD.
ENDCLASS.