ABAP-BarCode-1-HTML二维码打印及动态屏幕实现

很久很久。。。以前写的一个测试程序,主要是通过ABAP与HTML交互,编写JavaScript实现二维码及动态屏幕实现。

1.ABAP界面效果 

2.实现代码

*&---------------------------------------------------------------------*
*& Report  ZRICO_CHTML
*&
*&---------------------------------------------------------------------*
*& Dynamic screen   edit by rico
*&实例程序名:SAPHTML_EVENTS_DEMO
*&---------------------------------------------------------------------*

report  ycf_htmlviewer.

types : begin of y_html,
        dataset(255) type c,
        end of y_html.

data: e_data  type y_html,
      ts_data type standard table of y_html,
      e_user  type usr03,
      ok_code type sy-ucomm,
      w_uname type char20,
      w_url   type char255,
      p_tabnm like dd03l-tabname,
      o_tabnm like dd03l-tabname,
      bdstr type string,
      p_barcd type c,
      r_p1 type c value 'X',
      r_p2 type c.

data : ref_cont type ref to cl_gui_custom_container,
       ref_html type ref to cl_gui_html_viewer.

data: html_control type ref to cl_gui_html_viewer,
      my_container type ref to cl_gui_custom_container,
      prog_repid like sy-repid,
      ui_flag type i,                                       "#EC NEEDED
      edurl(2048),
      edframe(255),
      edaction(256),
      edgetdata(2048),
      edpostdataline(1024),
      myevent_tab type cntl_simple_events,
      myevent type cntl_simple_event,
      postdata_tab type cnht_post_data_tab,
      edquery_table type cnht_query_table,
      qtab type line of cnht_query_table.                   "#EC NEEDED

data:begin of ddtb occurs 0,
     check type c,
     tabname like dd03l-tabname,
     fieldname like dd03l-fieldname,
     rollname like dd03l-rollname,
     keyflag like dd03l-keyflag,
     position like dd03l-position,
     datatype like dd03l-datatype,
     leng like dd03l-leng,
     ddtext like dd03t-ddtext,
     value type string,
     end of ddtb.
data:ldtb like line of ddtb.

data:cuscontainer_con type ref to cl_gui_custom_container,
     alv_grid_con  type ref to cl_gui_alv_grid,
     ls_vari_con type disvariant,
     gt_fieldcat_con like lvc_s_fcat occurs 0 with header line,
     gs_layout_con type lvc_s_layo,
     lt_excl_func type ui_functions,
     g_repid like sy-repid,
     ls_stable type lvc_s_stbl.

data:cuscontainer_mx type ref to cl_gui_custom_container,
     alv_grid_mx  type ref to cl_gui_alv_grid,
     ls_vari_mx type disvariant,
     gt_fieldcat_mx like lvc_s_fcat occurs 0 with header line,
     gs_layout_mx type lvc_s_layo.

*----------------------------------------------------------------------*
*       CLASS cl_myevent_handler DEFINITION
*----------------------------------------------------------------------*
*
*----------------------------------------------------------------------*
class cl_myevent_handler definition.

  public section.
    methods: on_sapevent
               for event sapevent of cl_gui_html_viewer
                 importing action frame getdata postdata query_table.

endclass.                    "cl_myevent_handler DEFINITION
data: evt_receiver type ref to cl_myevent_handler.

"class define
class lcl_event_receiver definition deferred.

*----------------------------------------------------------------------*
*       CLASS lcl_event_receiver DEFINITION
*----------------------------------------------------------------------*
*
*----------------------------------------------------------------------*
class lcl_event_receiver definition.
  public section.
    methods:
      handle_double_click
      for event double_click of cl_gui_alv_grid
      importing e_row e_column es_row_no.
endclass.                    "lcl_event_receiver DEFINITION


data event_receiver type ref to lcl_event_receiver.

*----------------------------------------------------------------------*
*       class lcl_event_receiver implementation
*----------------------------------------------------------------------*
*
*----------------------------------------------------------------------*
class lcl_event_receiver implementation.
  method: handle_double_click.
    perform vp_double_clk using e_row
                                e_column
                                es_row_no.
  endmethod.                    "handle_double_click
endclass.                    "lcl_event_receiver implementation

data:gr_event_handler type ref to lcl_event_receiver.

*----------------------------------------------------------------------*
*       CLASS cl_myevent_handler IMPLEMENTATION
*----------------------------------------------------------------------*
*
*----------------------------------------------------------------------*
class cl_myevent_handler implementation.

  method on_sapevent.

    clear edaction.
    clear edframe.
    clear edgetdata.
    clear edpostdataline.

    edaction       = action.
    edframe        = frame.
    edgetdata      = getdata.
    postdata_tab   = postdata.
    if not postdata_tab is initial.
      read table postdata_tab index 1 into edpostdataline.
    endif.
    edquery_table  = query_table.

    case action.
*      WHEN 'SHOW_FRAMESET'.
*        PERFORM load_frame_set.
      when 'submit_form_as_post_method'.
        clear:bdstr.
        loop at ddtb into ldtb.
          read table edquery_table into qtab with key name = ldtb-fieldname .
          if sy-subrc = 0.
            ldtb-value = qtab-value.
            concatenate bdstr ldtb-ddtext ldtb-value into bdstr separated by space.
          endif.
          modify ddtb from ldtb.
          clear:ldtb,qtab.
        endloop.
      when others.
    endcase.
  endmethod.                    "on_sapevent

endclass.                    "cl_myevent_handler IMPLEMENTATION

data:user_id type usr01-bname.

initialization.
  g_repid = sy-repid.
  ls_stable-row = 'X'.
  ls_stable-col = 'X'.


start-of-selection.

  perform f_get_username.

  call screen 9000.
*&---------------------------------------------------------------------*
*&      Module  STATUS_9000  OUTPUT
*&---------------------------------------------------------------------*
*       PBO for 9000
*----------------------------------------------------------------------*
module status_9000 output.
  set pf-status 'STATUS'.

  perform f_genearte_html.

  perform html_container.

  perform tab_container.

endmodule.                 " STATUS_9000  OUTPUT
*&---------------------------------------------------------------------*
*&      Module  USER_COMMAND_9000  INPUT
*&---------------------------------------------------------------------*
*       PAI for screen 9000
*----------------------------------------------------------------------*
module user_command_9000 input.

  case ok_code.
    when ''.
      perform initial_data.
    when 'BACK'.
      leave to screen 0.
    when others.
  endcase.

endmodule.                 " USER_COMMAND_9000  INPUT

*&---------------------------------------------------------------------*
*&      Form  initial_data
*&---------------------------------------------------------------------*
*       text
*----------------------------------------------------------------------*
form initial_data.
  if o_tabnm <> p_tabnm.
    select * into corresponding fields of table ddtb
      from dd03l
     where tabname = p_tabnm
       and comptype <> 'S'.

    sort ddtb by position.

    loop at ddtb.
      if ddtb-rollname = ''.
        select single ddtext into ddtb-ddtext from dd03t where tabname = ddtb-tabname and ddlanguage = sy-langu and fieldname = ddtb-fieldname.
      else.
        select single ddtext into ddtb-ddtext from dd04t where rollname = ddtb-rollname and ddlanguage = sy-langu.
      endif.
      if ddtb-fieldname = 'MANDT'.
        delete ddtb.
      else.
        modify ddtb.
      endif.
      clear:ddtb.
    endloop.
    o_tabnm = p_tabnm.
  endif.
endform.                    "initial_data
*&---------------------------------------------------------------------*
*&      Form  F_GENEARTE_HTML
*&---------------------------------------------------------------------*
*       HTML code
*----------------------------------------------------------------------*

form f_genearte_html .

  clear:e_data.
  free ts_data.

  concatenate '<html><head> Dear:' sy-uname into e_data-dataset separated by space.
  append e_data to ts_data.

  if r_p1 = 'X'.
    e_data-dataset = '<br><img src="SAPLOGO.GIF" align=middle></br>'.
  elseif r_p2 = 'X'.
    e_data-dataset = '<br><img src="SAP_AG.GIF" align=middle></br>'.
  endif.
  append e_data to ts_data.

  e_data-dataset = '</head><body><meta http-equiv="Content-Type" content="text/html; charset=gb2312" />'.
  append e_data to ts_data.
  e_data-dataset = '<style type="text/css">'.
  append e_data to ts_data.
  e_data-dataset = '.div-c{ 200px; margin:0px;line-hight:10px;}'.
  append e_data to ts_data.
  e_data-dataset = '.div-a{ float:left;  50px; border:1px solid #999; height:60px;margin:20px;line-hight:10px;}'.
  append e_data to ts_data.
  e_data-dataset = '.div-b{ float:right; 120px; border:1px solid #999; height:60px;margin:20px;line-hight:10px;}'.
  append e_data to ts_data.
  e_data-dataset = '</style>'.
  append e_data to ts_data.
  "e_data-dataset = '<br><span style="font-size:500px">字体</span></br>'.  append e_data to ts_data.

  e_data-dataset = '<br><h3>Dynamic QR-CODE:'.
  append e_data to ts_data.

*********************************************************************************************
******* 以下为二维码生成
*********************************************************************************************
  if p_barcd = 'X'.
    e_data-dataset = '<script type="text/javascript" src="http://static.hdslb.com/js/jquery.min.js""></script>'.
    append e_data to ts_data.
    e_data-dataset = '<script type="text/javascript" src="http://static.hdslb.com/js/jquery.qrcode.min.js"></script>'.
    append e_data to ts_data.
    e_data-dataset = '<div id="code"></div>'.
    append e_data to ts_data.
    "unicode中文 由UTF-16转换为UTF-8
    e_data-dataset = '<script type="text/javascript">'.
    append e_data to ts_data.
    e_data-dataset = 'function utf16to8(str) {'.
    append e_data to ts_data.
    e_data-dataset = 'var out, i, len, c;'.
    append e_data to ts_data.
    e_data-dataset = 'out = "";'.
    append e_data to ts_data.
    e_data-dataset = 'len = str.length;'.
    append e_data to ts_data.
    e_data-dataset = 'for(i = 0; i < len; i++) {'.
    append e_data to ts_data.
    e_data-dataset = 'c = str.charCodeAt(i);'.
    append e_data to ts_data.
    e_data-dataset = 'if ((c >= 0x0001) && (c <= 0x007F)) {'.
    append e_data to ts_data.
    e_data-dataset = 'out += str.charAt(i);'.
    append e_data to ts_data.
    e_data-dataset = '} else if (c > 0x07FF) {'.
    append e_data to ts_data.
    e_data-dataset = 'out += String.fromCharCode(0xE0 | ((c >> 12) & 0x0F));'.
    append e_data to ts_data.
    e_data-dataset = 'out += String.fromCharCode(0x80 | ((c >>  6) & 0x3F));'.
    append e_data to ts_data.
    e_data-dataset = 'out += String.fromCharCode(0x80 | ((c >>  0) & 0x3F));'.
    append e_data to ts_data.
    e_data-dataset = '} else {'.
    append e_data to ts_data.
    e_data-dataset = 'out += String.fromCharCode(0xC0 | ((c >>  6) & 0x1F));'.
    append e_data to ts_data.
    e_data-dataset = 'out += String.fromCharCode(0x80 | ((c >>  0) & 0x3F));'.
    append e_data to ts_data.
    e_data-dataset = '}'.
    append e_data to ts_data.
    e_data-dataset = '}'.
    append e_data to ts_data.
    e_data-dataset = 'return out;'.
    append e_data to ts_data.
    e_data-dataset = '}'.
    append e_data to ts_data.
    e_data-dataset = '</script>'.
    append e_data to ts_data.

    "e_data-dataset = '<script>$("#code").qrcode("http://www.helloweba.com");</script>'. append e_data to ts_data.  "//任意字符串
    e_data-dataset = '<script>$("#code").qrcode({ '.
    append e_data to ts_data.
    e_data-dataset = '    render: "table", '.
    append e_data to ts_data.
    e_data-dataset = '     100, '.
    append e_data to ts_data.
    e_data-dataset = '    height:100, '.
    append e_data to ts_data.
    concatenate '    text: utf16to8("' bdstr '") ' into e_data-dataset.
    append e_data to ts_data.
    e_data-dataset = '}); </script>'.
    append e_data to ts_data.
  endif.
  e_data-dataset = '</h3><br>'.
  append e_data to ts_data.

*********************************************************************************************
******* 以下为动态字段
*********************************************************************************************
  concatenate 'Below is the dynamic fields from the data structure:' p_tabnm into e_data-dataset separated by space.
  append e_data to ts_data.

  e_data-dataset = '<form method=post action="sapevent:submit_form_as_post_method">'.
  append e_data to ts_data.
  loop at ddtb where check = 'X'.
    concatenate '<div class="div-c"><p><font size="2" color="blue">' ddtb-ddtext '</p></div>' into e_data-dataset.
    append e_data to ts_data.
    concatenate '<div class="div-c"><p><font size="2" color="blue"><input type="text" name="' ddtb-fieldname '" value="' ddtb-value '" maxlength="' ddtb-leng '"></p></div>' into e_data-dataset.
    append e_data to ts_data.
  endloop.
*  e_data-dataset = '<label for="meeting">日期:</label><input type="date" name="user_date" />'.
*  append e_data to ts_data.
  e_data-dataset = '<br><input type=submit value="submit input using post method"></br></form>'.
  append e_data to ts_data.

*********************************************************************************************
******* 以下为打印部分
*********************************************************************************************
*  e_data-dataset = '<script type="text/javascript" src="jquery-1.4.2.min.js"></script>'.
*  append e_data to ts_data.
*  e_data-dataset = '<script type="text/javascript" src="jquery.PrintArea.js"></script>'.
*  append e_data to ts_data.
*  e_data-dataset = '<script>'.
*  append e_data to ts_data.
*  e_data-dataset = '$(document).ready(function(){'.
*  append e_data to ts_data.
*  e_data-dataset = '$("input#biuuu_button").click(function(){'.
*  append e_data to ts_data.
*  e_data-dataset = '$("div#myPrintArea").printArea();'.
*  append e_data to ts_data.
*  e_data-dataset = '});'.
*  append e_data to ts_data.
*  e_data-dataset = '});'.
*  append e_data to ts_data.
*  e_data-dataset = '</script> '.
*  append e_data to ts_data.
*  e_data-dataset = '<input id="biuuu_button" type="button" value="打印"></input> '.
*  append e_data to ts_data.
*  e_data-dataset = '<div id="myPrintArea">.....文本打印部分.....</div>'.
*  append e_data to ts_data.

  e_data-dataset = '<input id="btnPrint" type="button" value="打印" onclick="javascript:window.print();"/>'.
  append e_data to ts_data.
  e_data-dataset = '<input id="btnPrint" type="button" value="打印预览" onclick=preview(1) />'.
  append e_data to ts_data.
  e_data-dataset = '<style type="text/css" media=print>'.
  append e_data to ts_data.
  e_data-dataset = '.noprint{display : none }'.
  append e_data to ts_data.
  e_data-dataset = '</style>'.
  append e_data to ts_data.
  e_data-dataset = '<p class="noprint">不需要打印的地方</p> '.
  append e_data to ts_data.
  e_data-dataset = '<script>'.
  append e_data to ts_data.
  e_data-dataset = 'function preview(oper)'.
  append e_data to ts_data.
  e_data-dataset = '{'.
  append e_data to ts_data.
  e_data-dataset = 'if (oper < 10) '.
  append e_data to ts_data.
  e_data-dataset = '{'.
  append e_data to ts_data.
  e_data-dataset = 'bdhtml=window.document.body.innerHTML;'.
  append e_data to ts_data.  "//获取当前页的html代码
  e_data-dataset = 'sprnstr="<!--startprint"+oper+"-->";'.
  append e_data to ts_data.  "//设置打印开始区域
  e_data-dataset = 'eprnstr="<!--endprint"+oper+"-->";'.
  append e_data to ts_data.  "//设置打印结束区域
  e_data-dataset = 'prnhtml=bdhtml.substring(bdhtml.indexOf(sprnstr)+18);'.
  append e_data to ts_data. "//从开始代码向后取html
  e_data-dataset = 'prnhtml=prnhtml.substring(0,prnhtml.indexOf(eprnstr)); '.
  append e_data to ts_data. "//从结束代码向前取html
  e_data-dataset = 'window.document.body.innerHTML=prnhtml;'.
  append e_data to ts_data.
  e_data-dataset = 'window.print();  '.
  append e_data to ts_data.
  e_data-dataset = 'window.document.body.innerHTML=bdhtml;  '.
  append e_data to ts_data.
  e_data-dataset = '} else {'.
  append e_data to ts_data.
  e_data-dataset = 'window.print();  '.
  append e_data to ts_data.
  e_data-dataset = '}'.
  append e_data to ts_data.
  e_data-dataset = '}'.
  append e_data to ts_data.
  e_data-dataset = '</script>'.
  append e_data to ts_data.
  e_data-dataset = '<p>XXXXX</p>'.
  append e_data to ts_data.
  e_data-dataset = '<!--startprint1-->要打印的内容<!--endprint1-->  '.
  append e_data to ts_data.

  e_data-dataset = '</body></html>'.
  append e_data to ts_data.

endform.                    " F_GENEARTE_HTML
*&---------------------------------------------------------------------*
*&      Form  F_GET_USERNAME
*&---------------------------------------------------------------------*
*       To get User name from Tcode
*----------------------------------------------------------------------*
form f_get_username .

  call function 'SUSR_USER_ADDRESS_READ'
    exporting
      user_name              = user_id
    importing
      user_usr03             = e_user
    exceptions
      user_address_not_found = 1
      others                 = 2.

  if sy-subrc = 0.

    concatenate e_user-name1
                e_user-name2
           into w_uname
           separated by space.

  endif.

endform.                    " F_GET_USERNAME
*&---------------------------------------------------------------------*
*&      Module  EXIT  INPUT
*&---------------------------------------------------------------------*
*       text
*----------------------------------------------------------------------*
module exit input.
  leave to screen 0.
endmodule.                 " EXIT  INPUT

*&---------------------------------------------------------------------*
*&      Form  tab_container
*&---------------------------------------------------------------------*
*       text
*----------------------------------------------------------------------*
form tab_container.
  clear:gt_fieldcat_con,gs_layout_con.
  refresh:gt_fieldcat_con.

  if cuscontainer_con is initial.
    create object cuscontainer_con
      exporting
        container_name              = 'TBCON'
      exceptions
        cntl_error                  = 1
        cntl_system_error           = 2
        create_error                = 3
        lifetime_error              = 4
        lifetime_dynpro_dynpro_link = 5
        others                      = 6.

    create object alv_grid_con "定义alv及所在容器
      exporting
        i_parent          = cuscontainer_con
      exceptions
        error_cntl_create = 1
        error_cntl_init   = 2
        error_cntl_link   = 3
        error_dp_create   = 4
        others            = 5.

    create object gr_event_handler.
    set handler gr_event_handler->handle_double_click for alv_grid_con.
  endif.

  if gt_fieldcat_con[] is initial.
    perform get_fieldcat using:
      "'TABNAME'    'T' '' '表名',
      'FIELDNAME'  'T' '' '字段名',
      'VALUE'      'T' '' '',
      'DDTEXT'     'T' '' '描述',
      'KEYFLAG'    'T' '' 'KEY'.
*       'POSITION'   'T' '' '序号',
*       'DATATYPE'   'T' '' '数据类型',
*       'LENG'       'T' '' '长度'.

    gs_layout_con-zebra = 'X'.
    gs_layout_con-cwidth_opt = 'X'.
    gs_layout_con-stylefname = 'FIELD_STYLE'.
    gs_layout_con-no_toolbar = 'X'.
    gs_layout_con-box_fname = 'CHECK'.
  endif.

  call method alv_grid_con->set_table_for_first_display " 显示alv
    exporting
      is_layout                     = gs_layout_con
      is_variant                    = ls_vari_con
      i_save                        = 'A'
      it_toolbar_excluding          = lt_excl_func
    changing
      it_fieldcatalog               = gt_fieldcat_con[]
      it_outtab                     = ddtb[]
    exceptions
      invalid_parameter_combination = 1
      program_error                 = 2
      too_many_lines                = 3
      others                        = 4 .

endform.                    "tab_container

*&---------------------------------------------------------------------*
*&      Form  get_fieldcat
*&---------------------------------------------------------------------*
*       text
*----------------------------------------------------------------------*
*      -->ZD         text
*      -->ZDBZ       text
*      -->ZDET       text
*      -->ZDMS       text
*----------------------------------------------------------------------*
form get_fieldcat using zd zdbz zdet zdms.
  if zdbz = 'T'.
    gt_fieldcat_con-fieldname  = zd.
    gt_fieldcat_con-reptext    = zdms.
    gt_fieldcat_con-checkbox   = zdet.
    append gt_fieldcat_con.
    clear:gt_fieldcat_con.
  elseif zdbz = 'M'.
    gt_fieldcat_mx-fieldname  = zd.
    gt_fieldcat_mx-reptext    = zdms.
    gt_fieldcat_mx-checkbox   = zdet.
    gt_fieldcat_mx-edit       = zdet.
    if zd = 'ZFDTX' or zd = 'ZFLNM'.
      gt_fieldcat_mx-edit    = 'X'.
    elseif zd = 'ZDFLG'.
      gt_fieldcat_mx-checkbox = 'X'.
    endif.
    append gt_fieldcat_mx.
    clear:gt_fieldcat_mx.
  endif.
endform.                    "get_fieldcat_con

*&---------------------------------------------------------------------*
*&      Form  vp_double_clk
*&---------------------------------------------------------------------*
*       text
*----------------------------------------------------------------------*
*      -->E_ROW      text
*      -->E_COLUMN   text
*      -->ES_ROW_NO  text
*----------------------------------------------------------------------*
form vp_double_clk using e_row  type lvc_s_row
                         e_column type lvc_s_col
                         es_row_no type lvc_s_roid.
  read table ddtb index es_row_no-row_id.
  if ddtb-check = 'X'.
    ddtb-check = ''.
  else.
    ddtb-check = 'X'.
  endif.
  modify ddtb index es_row_no-row_id transporting check.

  perform f_genearte_html.

  perform html_container.

endform.                    "vp_double_clk

*&---------------------------------------------------------------------*
*&      Form  html_container
*&---------------------------------------------------------------------*
*       text
*----------------------------------------------------------------------*
form html_container.

  if not ref_html is initial.
    "call method ref_html->free.
    clear:w_url.
    "free ref_html.
    "free ref_cont.
  endif.

  if ref_cont is initial.
    create object ref_cont
      exporting
        container_name              = 'HTMLCONT'
      exceptions
        cntl_error                  = 1
        cntl_system_error           = 2
        create_error                = 3
        lifetime_error              = 4
        lifetime_dynpro_dynpro_link = 5
        others                      = 6.
    if sy-subrc <> 0.
    endif.
  endif.

  if ref_html is initial.

    ui_flag = cl_gui_html_viewer=>uiflag_no3dborder.
    " + cl_gui_html_viewer=>uiflag_noiemenu.

    create object ref_html
      exporting
        parent             = ref_cont
        saphtmlp           = 'X'
        uiflag             = ui_flag
        lifetime           = cl_gui_html_viewer=>lifetime_dynpro
      exceptions
        cntl_error         = 1
        cntl_install_error = 2
        dp_install_error   = 3
        dp_error           = 4
        others             = 5.
    if sy-subrc <> 0.
    endif.


    myevent-eventid = ref_html->m_id_sapevent.
    myevent-appl_event = 'X'.
    append myevent to myevent_tab.
    call method ref_html->set_registered_events
      exporting
        events = myevent_tab.

    create object evt_receiver.

    set handler evt_receiver->on_sapevent for ref_html.

  endif.


  call method ref_html->load_mime_object
    exporting
      object_id  = 'HTMLCNTL_TESTHTM2_SAPLOGO'
      object_url = 'SAPLOGO.GIF'
    exceptions
      others     = 1.

  call method ref_html->load_mime_object
    exporting
      object_id  = 'HTMLCNTL_TESTHTM2_SAP_AG'
      object_url = 'SAP_AG.GIF'
    exceptions
      others     = 1.

  call method ref_html->load_data
    exporting
      type                 = 'text'
      subtype              = 'html'
    importing
      assigned_url         = w_url
    changing
      data_table           = ts_data
    exceptions
      dp_invalid_parameter = 1
      dp_error_general     = 2
      cntl_error           = 3
      others               = 4.
  if sy-subrc <> 0.
  endif.

  call method ref_html->show_url
    exporting
      url                    = w_url
    exceptions
      cntl_error             = 1
      cnht_error_not_allowed = 2
      cnht_error_parameter   = 3
      dp_error_general       = 4
      others                 = 5.
  if sy-subrc <> 0.
  endif.

*  call method  ref_html->do_refresh
*    exceptions cntl_error = 1.
*
*  call method cl_gui_cfw=>dispatch.
endform.                    "html_container
原文地址:https://www.cnblogs.com/ricoo/p/10070198.html