如何设置REUSE_ALV_GRID_DISPLAY'的单个单元格的是否可以输入

本文介绍如何使用SAP ABAP编程中的ALV (Application List Viewer) 控件来条件性地禁用或启用网格中的输入字段。通过具体示例展示了如何在ALV网格中设置特定单元格的可编辑状态,包括禁止整行输入及单个字段。

代码如下:具体说明参见红色说明(本例子是从订单明细提取两个字段的数据到内表)

REPORT ZALV_EDIT.
TYPE-POOLS: SLIS.
*- Fieldcatalog
DATA: IT_FIELDCAT TYPE LVC_T_FCAT.
DATA: X_FIELDCAT TYPE LVC_S_FCAT.
DATA: X_LAYOUT TYPE LVC_S_LAYO.

"第1步:用操作具体单元的是否可编辑的内表和工作区

DATA: LS_EDIT TYPE LVC_S_STYL,
      LT_EDIT TYPE LVC_T_STYL.
"第2步:在内表定义添加字段,用于控制具体行的具体单元是否可编辑
DATA: BEGIN OF IT_VBAP OCCURS 0,
  VBELN LIKE VBAP-VBELN,
  POSNR LIKE VBAP-POSNR,
  STYLE TYPE LVC_T_STYL, "FOR DISABLE
END OF IT_VBAP.
DATA: LS_OUTTAB LIKE LINE OF IT_VBAP.
SELECT VBELN  POSNR
  UP TO 100 ROWS
  INTO CORRESPONDING FIELDS OF TABLE IT_VBAP
FROM VBAP.

DATA:L_POS TYPE I VALUE 1.
CLEAR: L_POS.
L_POS = L_POS + 1.
X_FIELDCAT-SELTEXT = 'VBELN'.
X_FIELDCAT-FIELDNAME = 'VBELN'.
X_FIELDCAT-TABNAME = 'ITAB'.
X_FIELDCAT-COL_POS = L_POS.
X_FIELDCAT-EDIT = 'X'.
X_FIELDCAT-OUTPUTLEN = '10'.
x_fieldcat-ref_field = 'VBELN'.
x_fieldcat-ref_table = 'VBAK'.
APPEND X_FIELDCAT TO IT_FIELDCAT.

CLEAR X_FIELDCAT.
L_POS = L_POS + 1.
X_FIELDCAT-SELTEXT = 'POSNR'.
X_FIELDCAT-FIELDNAME = 'POSNR'.
X_FIELDCAT-TABNAME = 'ITAB'.
X_FIELDCAT-COL_POS = L_POS.
X_FIELDCAT-EDIT = 'X'.
X_FIELDCAT-OUTPUTLEN = '5'.
APPEND X_FIELDCAT TO IT_FIELDCAT.
CLEAR X_FIELDCAT.
L_POS = L_POS + 1.

"第3步:设置第六行两个单元都不能输入
SY-TABIX = 6.
LS_EDIT-FIELDNAME = 'VBELN'.
LS_EDIT-STYLE = CL_GUI_ALV_GRID=>MC_STYLE_DISABLED.
LS_EDIT-STYLE2 = SPACE.
LS_EDIT-STYLE3 = SPACE.
LS_EDIT-STYLE4 = SPACE.
LS_EDIT-MAXLEN = 10.
INSERT LS_EDIT INTO TABLE LT_EDIT.

LS_EDIT-FIELDNAME = 'POSNR'.
LS_EDIT-STYLE = CL_GUI_ALV_GRID=>MC_STYLE_DISABLED.
LS_EDIT-STYLE2 = SPACE.
LS_EDIT-STYLE3 = SPACE.
LS_EDIT-STYLE4 = SPACE.
LS_EDIT-MAXLEN = 6.
INSERT LS_EDIT INTO TABLE LT_EDIT.

INSERT LINES OF LT_EDIT INTO TABLE LS_OUTTAB-STYLE.

"第4步:将控制数据写到内表

MODIFY IT_VBAP INDEX SY-TABIX FROM LS_OUTTAB TRANSPORTING STYLE .

 

"设置第10行只有项目不能输入

clear LS_OUTTAB.
refresh LT_EDIT.
LS_EDIT-FIELDNAME = 'POSNR'.
LS_EDIT-STYLE = CL_GUI_ALV_GRID=>MC_STYLE_DISABLED.
LS_EDIT-STYLE2 = SPACE.
LS_EDIT-STYLE3 = SPACE.
LS_EDIT-STYLE4 = SPACE.
LS_EDIT-MAXLEN = 6.
INSERT LS_EDIT INTO TABLE LT_EDIT.

INSERT LINES OF LT_EDIT INTO TABLE LS_OUTTAB-STYLE.

SY-TABIX = 10.

"将控制数据写到内表

MODIFY IT_VBAP INDEX SY-TABIX FROM LS_OUTTAB TRANSPORTING STYLE .

"第5步:设置控制字段
X_LAYOUT-STYLEFNAME = 'STYLE'.

CALL FUNCTION 'REUSE_ALV_GRID_DISPLAY_LVC'
  EXPORTING
    I_CALLBACK_PROGRAM = SY-REPID
    IS_LAYOUT_LVC      = X_LAYOUT
    IT_FIELDCAT_LVC    = IT_FIELDCAT
  TABLES
    T_OUTTAB           = IT_VBAP[]
  EXCEPTIONS
    PROGRAM_ERROR      = 1
    OTHERS             = 2.
IF SY-SUBRC NE 0.
  MESSAGE ID SY-MSGID TYPE SY-MSGTY NUMBER SY-MSGNO
  WITH SY-MSGV1 SY-MSGV2 SY-MSGV3 SY-MSGV4.
ENDIF.

source url:https://www.sdn.sap.com/irj/sdn/wiki?path=/display/Snippets/Disable+or+Enable+Input+fields+Conditionally+In+ALV

<think>我们正在讨论ABAP中的ALV网格控件,特别是REUSE_ALV_GRID_DISPLAY_LVC函数模块。用户想要在修改单元格数据后刷新网格,同时保持当前的滚动位置(即用户正在查看的行和列不会因为刷新而改变)。 根据引用[^1],我们知道REUSE_ALV_GRID_DISPLAY_LVC是REUSE_ALV_GRID_DISPLAY的升级版,支持单元格级别的编辑。引用[^2]和[^3]提到了如何实现单元格编辑以及如何设置回调(通过设置`ls_setting-edt_cll_cb = 'X'`来启用单元格更改事件)。 但是,用户的问题是关于刷新网格并保持位置。在ALV中,当我们刷新网格时,默认情况下会回到初始位置(即第一行第一列)。为了保持位置,我们需要在刷新前保存当前的滚动位置,然后在刷新后恢复这个位置。 具体步骤: 1. 在刷新前获取当前的滚动位置(行和列)。 2. 刷新网格(重新显示ALV)。 3. 在刷新后设置之前保存的滚动位置。 在ABAP中,我们可以使用类`CL_GUI_ALV_GRID`的方法来获取和设置滚动位置。 实现思路: - 在定义ALV网格时,我们需要声明一个引用变量指向ALV网格对象(例如`gr_alv`)。 - 在刷新网格之前,调用`gr_alv->get_scroll_info()`方法获取当前的滚动信息(包括行和列)。 - 刷新网格(通常是通过调用函数重新显示ALV,但注意刷新时不能重新创建ALV控件,而应该使用`gr_alv->refresh_table_display()`方法)。 - 刷新之后,调用`gr_alv->set_scroll_info()`方法将之前保存的滚动位置设置回去。 但是,这里需要注意的是,用户使用的是`REUSE_ALV_GRID_DISPLAY_LVC`函数,这个函数在内部创建了ALV网格控件,并且我们通常不会直接持有该控件的引用。所以,我们需要在调用函数时获取这个引用,并保存起来。 具体步骤: 1. 在调用`REUSE_ALV_GRID_DISPLAY_LVC`时,通过参数`I_CALLBACK_PROGRAM`指定回调程序,同时使用`I_CALLBACK_TOP_OF_PAGE`或`I_CALLBACK_HTML_TOP_OF_PAGE`等参数并不是必须的。但是,为了获取ALV对象的引用,我们需要使用`I_CALLBACK_USER_COMMAND`或类似回调,或者使用`ES_EXIT_CAUSED_BY_USER`参数获取信息,但这些都不直接提供对象引用。 另一种方法:使用`cl_gui_alv_grid`的静态方法`get_instance_by_sub_id`或`get_instance_by_dynnr`来获取实例,但这种方法需要知道子容器ID或屏幕号,在函数模块内部创建的情况下可能比较困难。 实际上,在调用`REUSE_ALV_GRID_DISPLAY_LVC`时,我们可以通过参数`I_GRID`传递一个容器对象(自定义容器),这样我们就可以通过容器来获取ALV网格实例。但是,如果我们没有使用自定义容器(即使用函数默认创建的容器),那么我们就需要在回调中获取实例。 在ABAP中,有一个标准的做法:在`REUSE_ALV_GRID_DISPLAY_LVC`函数中,我们可以通过`I_CALLBACK_PROGRAM`指定回调程序,并在回调程序中使用`GET REFERENCE OF`来获取ALV对象。但更常见的做法是使用类`CL_GUI_CFW`的静态方法`GET_ALV_GRID`来获取当前屏幕上的ALV网格实例。 然而,在函数模块`REUSE_ALV_GRID_DISPLAY_LVC`中,它内部创建了一个ALV网格控件,我们可以通过设置一个导出参数来获取这个网格对象的引用?查看函数文档,我们发现它并没有直接导出网格对象。 因此,我们需要改变方法:使用`REUSE_ALV_GRID_DISPLAY_LVC`的一个变通方式,即先创建一个自定义容器,然后将这个容器传递给函数,这样我们就可以通过容器来获取ALV网格对象。 具体步骤: 1. 在屏幕上创建一个自定义容器控件(比如在屏幕绘制器中创建一个容器区域,并命名,例如`CC_ALV`)。 2. 在程序中使用`DATA: gr_container TYPE REF TO cl_gui_custom_container, gr_alv TYPE REF TO cl_gui_alv_grid.`声明引用。 3. 实例化容器:`CREATE OBJECT gr_container EXPORTING container_name = 'CC_ALV'.` 4. 然后使用函数`REUSE_ALV_GRID_DISPLAY_LVC`时,传递容器参数: ```abap CALL FUNCTION 'REUSE_ALV_GRID_DISPLAY_LVC' EXPORTING i_callback_program = sy-repid i_grid_settings = ls_setting is_layout_lvc = ls_layout it_fieldcat_lvc = lt_fieldcat i_callback_user_command = 'USER_COMMAND' i_save = 'A' TABLES t_outtab = gt_data EXCEPTIONS program_error = 1 OTHERS = 2. ``` 注意:这里我们没有传递容器,所以函数会自己创建容器(默认是弹出一个对话框)。为了能够控制ALV对象,我们需要传递容器参数`I_CONTAINER`,即: ```abap CALL FUNCTION 'REUSE_ALV_GRID_DISPLAY_LVC' EXPORTING i_callback_program = sy-repid i_container = gr_container " 传递自定义容器 ... ``` 但是,这个函数模块并没有`I_CONTAINER`参数!实际上,`REUSE_ALV_GRID_DISPLAY_LVC`函数内部会创建容器,并不支持传入外部容器。 所以,我们需要使用另一种方法:使用`CL_GUI_ALV_GRID`类来创建ALV网格。这样我们就可以完全控制网格对象。 步骤: 1. 创建自定义容器。 2. 使用`CL_GUI_ALV_GRID`的构造函数创建ALV网格,并关联到容器。 3. 调用`SET_TABLE_FOR_FIRST_DISPLAY`方法显示数据。 这样,我们就拥有了ALV网格对象的引用(`gr_alv`),然后就可以在需要刷新时调用`gr_alv->refresh_table_display()`方法,并且在刷新前后处理滚动位置。 关于刷新并保持位置: - 在用户修改单元格后,会触发`DATA_CHANGED`事件(需要设置`ls_setting-edt_cll_cb = 'X'`)。 - 在事件处理程序`ON_DATA_CHANGED`中,我们可以保存修改到内表(通过`ER_DATA_CHANGED`对象的`MOD_CELLS`属性获取修改),然后刷新网格。 但是,刷新网格时,我们希望在刷新后保持滚动位置。因此,在刷新前保存滚动位置,刷新后恢复。 具体代码框架: ```abap * 定义类来处理事件 CLASS lcl_event_handler DEFINITION. PUBLIC SECTION. METHODS: handle_data_changed FOR EVENT data_changed OF cl_gui_alv_grid IMPORTING er_data_changed. ENDCLASS. CLASS lcl_event_handler IMPLEMENTATION. METHOD handle_data_changed. " 在此处理数据修改,更新内表 " 然后刷新网格 DATA: ls_scroll TYPE lvc_s_scroll. " 保存当前滚动位置 gr_alv->get_scroll_info( IMPORTING e_row = lv_row e_col = lv_col ). " 或者使用结构ls_scroll gr_alv->get_scroll_info( IMPORTING es_scroll = ls_scroll ). " 更新内表(根据er_data_changed的修改) " ... 这里需要编写代码将修改从er_data_changed应用到内表gt_data " 刷新ALV gr_alv->refresh_table_display( ). " 恢复滚动位置 gr_alv->set_scroll_info( EXPORTING is_scroll = ls_scroll ). ENDMETHOD. ENDCLASS. ``` 注意:在刷新后恢复滚动位置,但是刷新操作可能会改变网格的结构(比如排序、过滤),所以需要确保在刷新前保存的滚动位置在刷新后仍然有效(即行号没有因为排序等操作而改变)。如果内表数据顺序改变,那么原先的行号指向的行可能已经改变位置。所以,更安全的方法是保存关键字段的值,然后刷新后找到该行在刷新后的行号,再滚动到该行。 但是,如果只是简单的单元格修改(不改变行顺序),那么直接保存行索引(行号)即可。 因此,解决方案总结如下: 1. 使用类`CL_GUI_ALV_GRID`创建ALV网格,以便持有对象引用。 2. 注册事件`data_changed`,并设置`edt_cll_cb = 'X'`。 3. 在事件处理程序中,保存当前滚动位置(行号和列号),更新内表数据,刷新网格,然后恢复滚动位置。 但是,用户要求使用`REUSE_ALV_GRID_DISPLAY_LVC`函数。如果我们无法通过该函数获取网格对象,那么我们就不能使用上述方法。那么,有没有办法在`REUSE_ALV_GRID_DISPLAY_LVC`创建的网格中实现呢? 实际上,我们可以通过类`CL_GUI_CFW`获取当前屏幕上的ALV网格实例。但是,这种方法依赖于屏幕上的第一个ALV网格,如果只有一个ALV网格,可以这样获取: ```abap DATA: gr_grid TYPE REF TO cl_gui_alv_grid. gr_grid ?= cl_gui_alv_grid=>get_instance( ). IF gr_grid IS BOUND. " 然后就可以调用方法 ENDIF. ``` 但是,请注意,`get_instance`方法在没有网格时会返回空。而且,如果有多个网格,还需要使用`get_instance_by_sub_id`等。 因此,在`REUSE_ALV_GRID_DISPLAY_LVC`之后,我们可以这样获取网格对象: ```abap DATA: lo_grid TYPE REF TO cl_gui_alv_grid. lo_grid = cl_gui_alv_grid=>get_instance( ). IF lo_grid IS BOUND. " 保存滚动位置 lo_grid->get_scroll_info( ... ) " 刷新 lo_grid->refresh_table_display( ) " 恢复滚动位置 lo_grid->set_scroll_info( ... ) ENDIF. ``` 但是,在事件处理函数中,我们也可以使用`GET REFERENCE OF`来获取事件来源的对象(即触发事件的网格)。在事件处理程序方法中,可以使用参数`sender`来获取事件来源的网格对象。 因此,在`REUSE_ALV_GRID_DISPLAY_LVC`中,我们可以通过事件处理来获取网格对象: 1. 设置单元格编辑回调(`edt_cll_cb = 'X'`)。 2. 定义`DATA_CHANGED`事件的事件处理程序(通过设置`I_CALLBACK_DATA_CHANGED`参数)。 3. 在事件处理程序中,参数`ER_EVENT_DATA`(或`EO_DATA_CHANGED`,取决于事件类型)会传递一个对象,这个对象就是`CL_GUI_ALV_GRID`的实例?实际上,在`REUSE_ALV_GRID_DISPLAY_LVC`中,事件处理程序`I_CALLBACK_DATA_CHANGED`被调用时,系统会传递一个类型为`CL_ALV_CHANGED_DATA_PROTOCOL`的对象(该对象包含了修改的单元格信息),但它不直接提供网格对象。 所以,我们还是要通过其他方式获取网格对象。 结论:为了可靠地获取ALV网格对象,建议使用自定义容器和`CL_GUI_ALV_GRID`类来创建ALV网格,而不是使用`REUSE_ALV_GRID_DISPLAY_LVC`函数。这样我们就可以完全控制网格对象。 但是,如果用户坚持使用`REUSE_ALV_GRID_DISPLAY_LVC`,那么我们可以尝试在事件处理程序中使用`cl_gui_alv_grid=>get_instance()`来获取网格对象。由于函数模块默认会创建一个网格,所以我们可以这样获取: ```abap DATA: lo_grid TYPE REF TO cl_gui_alv_grid. lo_grid = cl_gui_alv_grid=>get_instance( ). IF lo_grid IS BOUND. " 成功获取 ENDIF. ``` 因此,在回调函数`I_CALLBACK_DATA_CHANGED`中,我们可以这样写: ```abap FORM data_changed USING p_data_changed TYPE REF TO cl_alv_changed_data_protocol. DATA: lo_grid TYPE REF TO cl_gui_alv_grid, ls_scroll TYPE lvc_s_scroll. " 获取网格实例 lo_grid = cl_gui_alv_grid=>get_instance( ). IF lo_grid IS BOUND. " 保存滚动位置 lo_grid->get_scroll_info( IMPORTING es_scroll = ls_scroll ). " 将修改应用到内表(根据p_data_changed) " ... " 刷新网格 lo_grid->refresh_table_display( is_stable = VALUE #( row = abap_true col = abap_true ) ). " 这里的is_stable参数可以保持稳定性,但可能还不够 " 恢复滚动位置 lo_grid->set_scroll_info( EXPORTING is_scroll = ls_scroll ). ENDIF. ENDFORM. ``` 但请注意,在刷新时使用`is_stable`参数可以告诉网格刷新时保持滚动位置,但实际上,在`refresh_table_display`方法中,有一个参数`IS_STABLE`,设置`row`和`col`为`abap_true`可以尝试保持稳定性,但文档说它只能保持滚动位置到可能的程度(比如行还在,没有过滤掉)。所以我们不需要自己手动设置滚动位置?但是,根据经验,仅仅设置`IS_STABLE`不一定能完全保持位置,尤其是当修改导致行高变化等情况时。 因此,手动保存和恢复滚动位置是更可靠的方法。 最终解决方案: 在`REUSE_ALV_GRID_DISPLAY_LVC`中,设置单元格编辑回调(`ls_setting-edt_cll_cb = 'X'`),并设置`I_CALLBACK_DATA_CHANGED`为`'DATA_CHANGED'`。 在`DATA_CHANGED`表单中,使用`cl_gui_alv_grid=>get_instance()`获取网格实例,然后保存滚动位置,修改内表,刷新网格,最后恢复滚动位置。 注意:刷新前需要将修改保存到内表,否则刷新后修改会丢失。如何保存?通过事件参数`p_data_changed`(类型为`cl_alv_changed_data_protocol`)可以获取修改的单元格信息。 示例代码: ```abap FORM data_changed USING p_data_changed TYPE REF TO cl_alv_changed_data_protocol. DATA: lo_grid TYPE REF TO cl_gui_alv_grid. DATA: ls_scroll TYPE lvc_s_scroll. lo_grid = cl_gui_alv_grid=>get_instance( ). IF lo_grid IS BOUND. " 保存滚动位置 lo_grid->get_scroll_info( IMPORTING es_scroll = ls_scroll ). " 将修改应用到内表 " 遍历p_data_changed->mt_mod_cells,修改内表gt_data LOOP AT p_data_changed->mt_mod_cells ASSIGNING FIELD-SYMBOL(<fs_mod_cell>). " 修改内表对应行的字段 ASSIGN gt_data[ <fs_mod_cell>-row_id ] TO FIELD-SYMBOL(<fs_line>). IF sy-subrc = 0. ASSIGN COMPONENT <fs_mod_cell>-fieldname OF STRUCTURE <fs_line> TO FIELD-SYMBOL(<fs_field>). IF sy-subrc = 0. <fs_field> = <fs_mod_cell>-value. ENDIF. ENDIF. ENDLOOP. " 刷新网格 lo_grid->refresh_table_display( ). " 恢复滚动位置 lo_grid->set_scroll_info( EXPORTING is_scroll = ls_scroll ). ENDIF. ENDFORM. ``` 注意:这里没有考虑修改验证等,实际应用中需要根据业务逻辑进行验证。 另外,如果修改导致行顺序变化(例如,修改了排序字段),那么保存的行号可能不再指向同一行。在这种情况下,可能需要根据内容重新定位,但用户问题中没有提到这种情况,所以我们假设只是普通字段修改。 总结:通过获取ALV网格实例,在刷新前保存滚动位置,刷新后恢复,可以实现刷新网格并保持位置。 因此,给用户的回答应该包含上述代码框架,并给出使用`REUSE_ALV_GRID_DISPLAY_LVC`时的事件处理设置方法。 </think>在ABAP中使用`REUSE_ALV_GRID_DISPLAY_LVC`修改单元格数据后刷新网格并保持位置的解决方案如下: ### 核心步骤 1. **启用单元格编辑功能** 调用函数前需设置`I_GRID_SETTINGS-EDT_CLL_CB = 'X'`,启用单元格修改回调: ```abap DATA: ls_grid_settings TYPE lvc_s_glay. ls_grid_settings-edt_cll_cb = 'X'. " 启用单元格编辑回调[^3] ``` 2. **实现DATA_CHANGED事件处理** 在`I_CALLBACK_DATA_CHANGED`指定的子程式中处理修改: ```abap FORM data_changed USING p_changed TYPE REF TO cl_alv_changed_data_protocol. " 1. 保存当前滚动位置 DATA(lo_grid) = cl_gui_alv_grid=>get_instance( ). DATA: ls_scroll TYPE lvc_s_scroll. lo_grid->get_scroll_info( IMPORTING es_scroll = ls_scroll ). " 2. 将修改应用到内表 LOOP AT p_changed->mt_mod_cells ASSIGNING FIELD-SYMBOL(<fs_mod>). MODIFY gt_data INDEX <fs_mod>-row_id FROM VALUE #( ( ... ) ) " 根据修改更新内表 TRANSPORTING <字段名>. ENDLOOP. " 3. 刷新网格并恢复位置 lo_grid->refresh_table_display( is_stable = VALUE #( row = abap_true col = abap_true ) " 保持稳定性 ). lo_grid->set_scroll_info( is_scroll = ls_scroll ). " 恢复滚动位置 ENDFORM. ``` 3. **调用函数时传递参数** ```abap CALL FUNCTION 'REUSE_ALV_GRID_DISPLAY_LVC' EXPORTING i_callback_data_changed = 'DATA_CHANGED' " 事件处理程式[^2] i_grid_settings = ls_grid_settings is_layout_lvc = ls_layout it_fieldcat_lvc = lt_fieldcat TABLES t_outtab = gt_data. ``` ### 关键点说明 1. **滚动位置保持** - 使用`get_scroll_info()`保存滚动状态(行/列位置)[^2] - 刷新后通过`set_scroll_info()`恢复位置 - `is_stable`参数确保刷新时界面稳定 2. **数据更新逻辑** - 通过`mt_mod_cells`获取修改的单元格列表[^3] - 需同步更新底层内表(`gt_data`),否则刷新后修改会丢失 3. **LVC特性利用** - 使用`REUSE_ALV_GRID_DISPLAY_LVC`(非旧版)支持单元格级操作[^1] - 结合`CL_GUI_ALV_GRID`方法实现精细控制 > 此方案通过事件捕获修改->保存界面状态->更新数据->恢复界面的流程,在保障数据一致性的同时维持用户体验[^2][^3]。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值