上周TW总公司的IT按照董事长的指示,但凡杂项请购单里面内部订单号如果没有开立开发执行单的需做卡控,限制开单。于是TW IT就在系统画面ME51N里面做了用户出口开发,如果系统检测到内部订单没有开发执行单,则提示并不继续往下。因为TW那边的卡控文字说明是繁体的,所以OA调用了RFC申请开立请购单之后就会返回这个错误信息,而且是乱码,如下图:

       

       看到这个乱码我还以为是出了什么异常,因为系统抛出异常类是:RfcCommunicationException,到处找原因,搞得满头包。后面只好到正式环境试着用OA的数据开立请购单试试,结果系统提示开发执行单不存在,这才知道原因所在,因此对这个乱码感到很费劲,于是解决之。

       我们在调用RFC前的配置信息里面可以规定编码,如下红字所示:       

       public class MyBackendConfig : IDestinationConfiguration

        {

            public RfcConfigParameters GetParameters(String destinationName)

            {

                if ("PRD_000".Equals(destinationName))

                {

                    RfcConfigParameters parms = new RfcConfigParameters();

                    parms.Add(RfcConfigParameters.AppServerHost, "192.168.1.3");

                    parms.Add(RfcConfigParameters.SystemNumber, "00");

                    parms.Add(RfcConfigParameters.User, "MENGXIN");

                    parms.Add(RfcConfigParameters.Password, "×××");

                    parms.Add(RfcConfigParameters.Client, "888");

                    parms.Add(RfcConfigParameters.Language, "ZH");

                    parms.Add(RfcConfigParameters.PoolSize, "5");

                    parms.Add(RfcConfigParameters.MaxPoolSize, "10");

                    parms.Add(RfcConfigParameters.IdleTimeout, "60");

                    parms.Add(RfcConfigParameters.Codepage, "8300");

                    return parms;

                }

                else return null;

            }

            public bool ChangeEventsSupported()

            {

                return false;

            }

            public event RfcDestinationManager.ConfigurationChangeHandler ConfigurationChanged;

        }

       这样规定了这个编码为8300(繁体),于是系统正确提示了信息:

       

       关于这个Codepage的代码含义,如下表:       

       

        至此,乱码问题完全解决~

       PS:.NET连接SAP系统专题 就到此结束了。欢迎各位朋友一起交流指正...

posted @ 2012-02-09 22:16 梦心 阅读(13) 评论(0) 编辑

      今天临下班之前意外接到一个电话,来自深圳华为的招聘电话,HR人员问我是否有意向去深圳发展,主要做流程应用管理,做Oracle实施,以后可能还会外派去国外。华为在我眼中一直有两个代名词:工作之辛苦和待遇之丰厚。所以说这个是非常好的工作机会,但着眼于目前,发现自己有一些东西难以舍弃。一者是自己做了很久的SAP工作,也深深喜欢上SAP开发,同时本身自己也对Oracle不太了解,就这样转过去会不太适应。二者是自己离不开厦门,离不开她。如今爱情尚未结果,如何能离开的了?当然,能不能聘上是另外一回事了...

      其实自己已经挺不太喜欢现在的TTE了,不仅严重不公平,而且流程巨长,很多权限和开发流程都牢牢被掌握在总公司,很多东西得不到很好的决定和规划。哎,其实挺感恩这里的,但如果太多不爽的因素,自己也会被逼得“反”。已经有很多人在思量下一步计划了...

posted @ 2012-02-03 23:29 梦心 阅读(82) 评论(1) 编辑

看上时间,明天就是要上班了。

回想这一过年,过的匆忙,也很压抑。到处充斥着女朋友和结婚的话题,搞得好像我人生就剩这两个主题一样。

我知道我在回避很多问题,但也不可救药的带来了很多的问题。

我觉得:

1、明年工作的重心将从CODING改为前端操作和img设置上来。有点厌烦TTE了,我看透了很多不公平的东西。所以自己得多多努力学习一下,总不能什么都不懂。希望来年更加把劲去学习!

2、博客园会继续更新下去,也许这里是我汇集学习成果的地方。我不能荒废我这里的一片成果。

3、爱情会结晶成果吗?我不知道,不敢想,唯有自己去做到最好。至于后面结果如何,我自是希望会有收获。但这不是我能决定的。

暂时就想到这里,洗洗睡吧。

 

posted @ 2012-01-30 00:35 梦心 阅读(35) 评论(2) 编辑

在SAP的标准画面中,树控件都是随处可见,在一些特殊的应用中更有用处, 显得专业。

在SE51设计界面里,并没有直观的树控件,只是一个容器,要实现树功能还得在程序中用面向对象的方法实现。

本文就简要介绍一下应用。

定义部分:

*内表部分

DATA:IT_TREE LIKE STANDARD TABLE OF ZOA_REG WITH HEADER LINE.
    DATA IT_TREE1 LIKE IT_TREE.
FIELD-SYMBOLS <FS1>  LIKE LINE OF IT_TREE.


*   ----->TREE

CLASS LCL_APPLICATION DEFINITION DEFERRED.
CLASS CL_GUI_CFW DEFINITION LOAD.
TYPESNODE_TABLE_TYPE LIKE STANDARD TABLE OF MTREESNODE
WITH DEFAULT KEY.
DATA:NODE_TABLE TYPE NODE_TABLE_TYPE .
* CAUTION: MTREESNODE IS THE NAME OF THE NODE STRUCTURE WHICH MUST
* BE DEFINED BY THE PROGRAMMER. DO NOT USE MTREESNODE!
DATAG_APPLICATION TYPE REF TO LCL_APPLICATION,
      G_CUSTOM_CONTAINER TYPE REF TO CL_GUI_CUSTOM_CONTAINER,
      G_TREE TYPE REF TO CL_GUI_SIMPLE_TREE.
DATAG_EVENT(30),
      G_NODE_KEY TYPE TV_NODEKEY.

*文本控件
DATA:USERNO(20),
     USERNAME(20),
     PASSWORD(20),
     BRANCH(20),
     UNIT(20),
     MAIL(20),
     TDATE(20),
     SEX(20),
     NOQUIT,
     QUIT.

2、状态及功能

*&---------------------------------------------------------------------*
*&      MODULE  STATUS_0100  OUTPUT
*&---------------------------------------------------------------------*
*       TEXT
*----------------------------------------------------------------------*
MODULE STATUS_0100 OUTPUT.
  SET PF-STATUS 'PFSTATUS'.
  SET TITLEBAR 'TITLE'.

ENDMODULE.                 " STATUS_0100  OUTPUT
*&---------------------------------------------------------------------*
*&      MODULE  USER_COMMAND_0100  INPUT
*&---------------------------------------------------------------------*
*       TEXT
*----------------------------------------------------------------------*
MODULE USER_COMMAND_0100 INPUT.
  CASE SY-UCOMM.
    WHEN 'BACK' OR 'EXIT' OR 'CANCEL'.
      LEAVE.
  ENDCASE.
ENDMODULE.                 " USER_COMMAND_0100  INPUT

3、定义双击事件的定义和方法体:


*----------------------------------------------------------------------*
*       CLASS LCL_APPLICATION DEFINITION
*----------------------------------------------------------------------*
*
*----------------------------------------------------------------------*
CLASS LCL_APPLICATION DEFINITION.

  PUBLIC SECTION.
    METHODS:
    HANDLE_NODE_DOUBLE_CLICK
    FOR EVENT NODE_DOUBLE_CLICK
    OF CL_GUI_SIMPLE_TREE
    IMPORTING NODE_KEY.

ENDCLASS.                    "LCL_APPLICATION DEFINITION

*----------------------------------------------------------------------*
*       CLASS LCL_APPLICATION IMPLEMENTATION
*----------------------------------------------------------------------*
*
*----------------------------------------------------------------------*
CLASS LCL_APPLICATION IMPLEMENTATION.

  METHOD  HANDLE_NODE_DOUBLE_CLICK.
    FIELD-SYMBOLS <NODE> TYPE mtreesnode.

    " THIS METHOD HANDLES THE NODE DOUBLE CLICK EVENT OF THE TREE
    " CONTROL INSTANCE

    READ TABLE NODE_TABLE ASSIGNING <NODE>
    WITH KEY NODE_KEY =  NODE_KEY .

    IF NODE_KEY  CS 'ZOA_REG'.                  "點擊父節點不做處理
      MESSAGE <NODE>-TEXT TYPE 'I'.
    ENDIF.

    READ TABLE IT_TREE INTO IT_TREE1 WITH KEY USERNO <NODE>-TEXT.
    IF SY-SUBRC 0.
      USERNO =  IT_TREE1-USERNO.
      "USERNAME =  IT_TREE1-NAME.
      PASSWORD =  IT_TREE1-PASS.
      BRANCH IT_TREE1-BRANCH.
      UNIT =  IT_TREE1-UNIT.
      MAIL =  IT_TREE1-MAIL.
      TDATE =  IT_TREE1-TDATE.
      SEX =  IT_TREE1-SEX.

      IF IT_TREE1-STATE  '在职'.
        NOQUIT 'X'.
        QUIT ''.
      ELSE.
        NOQUIT ''.
        QUIT 'X'.
      ENDIF.
    ELSE.
      MESSAGE NODE_KEY TYPE 'I'.
    ENDIF.
   ENDMETHOD.                    "HANDLE_NODE_DOUBLE_CLICK
ENDCLASS.                    "LCL_APPLICATION IMPLEMENTATION

4、定义填充TREE控件的节点


*&---------------------------------------------------------------------*
*&      FORM  GET_PRO_ITEM
*&---------------------------------------------------------------------*
*       TEXT
*----------------------------------------------------------------------*
*  -->  P1        TEXT
*  <--  P2        TEXT
*----------------------------------------------------------------------*
FORM GET_PRO_ITEM .
  PERFORM GET_DATA .
  IF G_TREE IS INITIAL.
    PERFORM CREATE_AND_INIT_TREE.
  ENDIF.
ENDFORM.                    " GET_PRO_ITEM

*&---------------------------------------------------------------------*
*&      FORM  CREATE_AND_INIT_TREE
*&---------------------------------------------------------------------*
*       初始化树
*----------------------------------------------------------------------*
*  -->  P1        TEXT
*  <--  P2        TEXT
*----------------------------------------------------------------------*
FORM CREATE_AND_INIT_TREE .

  DATA:  EVENTS TYPE CNTL_SIMPLE_EVENTS,
        EVENT TYPE CNTL_SIMPLE_EVENT.

* CREATE A CONTAINER FOR THE TREE CONTROL
  CREATE OBJECT G_CUSTOM_CONTAINER
  EXPORTING " THE CONTAINER IS LINKED TO THE CUSTOM CONTROL WITH THE
    " NAME 'TREE_CONTAINER' ON THE DYNPRO
    CONTAINER_NAME              'THE_TREE'          "填充到画面上的容器里
  EXCEPTIONS
    CNTL_ERROR                  1
    CNTL_SYSTEM_ERROR           2
    CREATE_ERROR                3
    LIFETIME_ERROR              4
    LIFETIME_DYNPRO_DYNPRO_LINK 5.


  IF SY-SUBRC <> 0.
    MESSAGE ID SY-MSGID TYPE SY-MSGTY NUMBER SY-MSGNO
    WITH SY-MSGV1 SY-MSGV2 SY-MSGV3 SY-MSGV4.
  ENDIF.

* CREATE A TREE CONTROL
  CREATE OBJECT G_TREE
    EXPORTING
      PARENT                      G_CUSTOM_CONTAINER
      NODE_SELECTION_MODE         CL_GUI_SIMPLE_TREE=>NODE_SEL_MODE_SINGLE   "单选模式
    EXCEPTIONS
      LIFETIME_ERROR              1" SINGLE NODE SELECTIONIS USED
      CNTL_SYSTEM_ERROR           2
      CREATE_ERROR                3
      FAILED                      4
      ILLEGAL_NODE_SELECTION_MODE 5.
  IF SY-SUBRC <> 0.
    MESSAGE ID SY-MSGID TYPE SY-MSGTY NUMBER SY-MSGNO
    WITH SY-MSGV1 SY-MSGV2 SY-MSGV3 SY-MSGV4.
  ENDIF.

* DEFINE THE EVENTS WHICH WILL BE PASSED TO THE BACKEND
  " NODE DOUBLE CLICK
  EVENT-EVENTID CL_GUI_SIMPLE_TREE=>EVENTID_NODE_DOUBLE_CLICK.  "定义双击事件
  EVENT-APPL_EVENT 'X'" PROCESS PAI IF EVENT OCCURS
  APPEND EVENT TO EVENTS.

  CALL METHOD G_TREE->SET_REGISTERED_EVENTS
    EXPORTING
      EVENTS                    EVENTS
    EXCEPTIONS
      CNTL_ERROR                1
      CNTL_SYSTEM_ERROR         2
      ILLEGAL_EVENT_COMBINATION 3.
  IF SY-SUBRC <> 0.
    MESSAGE ID SY-MSGID TYPE SY-MSGTY NUMBER SY-MSGNO
    WITH SY-MSGV1 SY-MSGV2 SY-MSGV3 SY-MSGV4.
  ENDIF.

* ASSIGN EVENT HANDLERS IN THE APPLICATION CLASS TO EACH DESIRED EVENT
  CREATE OBJECT G_APPLICATION.
  SET HANDLER G_APPLICATION->HANDLE_NODE_DOUBLE_CLICK FOR G_TREE.   "实现双击

   PERFORM BUILD_NODE_TABLE USING NODE_TABLE.    "填充树的节点

   CALL METHOD G_TREE->ADD_NODES
    EXPORTING
      TABLE_STRUCTURE_NAME           'MTREESNODE'
      NODE_TABLE                     NODE_TABLE
    EXCEPTIONS
      FAILED                         1
      ERROR_IN_NODE_TABLE            2
      DP_ERROR                       3
      TABLE_STRUCTURE_NAME_NOT_FOUND 4
      OTHERS                         5.
  IF SY-SUBRC <> 0.
    MESSAGE ID SY-MSGID TYPE SY-MSGTY NUMBER SY-MSGNO
    WITH SY-MSGV1 SY-MSGV2 SY-MSGV3 SY-MSGV4.
  ENDIF.

*設置TREE CONTROL中的節點默認為展開狀態
  CALL METHOD G_TREE->EXPAND_NODE
  EXPORTING
    NODE_KEY            G_NODE_KEY
*      LEVEL_COUNT         =
*      EXPAND_SUBTREE      =
  EXCEPTIONS
    FAILED              1
    ILLEGAL_LEVEL_COUNT 2
    CNTL_SYSTEM_ERROR   3
    NODE_NOT_FOUND      4
    CANNOT_EXPAND_LEAF  5
    OTHERS              6.
  IF SY-SUBRC <> 0."...
    MESSAGE ID SY-MSGID TYPE SY-MSGTY NUMBER SY-MSGNO
    WITH SY-MSGV1 SY-MSGV2 SY-MSGV3 SY-MSGV4.
  ENDIF.

ENDFORM.                    " CREATE_AND_INIT_TREE
*&---------------------------------------------------------------------*
*&      FORM  BUILD_NODE_TABLE
*&---------------------------------------------------------------------*
*       TEXT
*----------------------------------------------------------------------*
*      -->P_NODE_TABLE  TEXT
*----------------------------------------------------------------------*
FORM BUILD_NODE_TABLE  USING   NODE_TABLE TYPE NODE_TABLE_TYPE.
  DATA:NODE TYPE MTREESNODE.
  DATAL_INDEX LIKE SY-TABIX.

**設置父節點屬性
  NODE-NODE_KEY 'ZOA_REG'.
  G_NODE_KEY NODE-NODE_KEY.
  CLEAR NODE-RELATKEY.      " SPECIAL CASE: A ROOT NODE HAS NO PARENT
  CLEAR NODE-RELATSHIP.
  NODE-HIDDEN ''.         " THE NODE IS VISIBLE,
  NODE-DISABLED ''.       " SELECTABLE,
  NODE-ISFOLDER 'X'.      " A FOLDER.
*  NODE-N_IMAGE = 'X'.
*  NODE-EXP_IMAGE = 'X'.
  NODE-STYLE CL_GUI_SIMPLE_TREE=>STYLE_DEFAULT.
*  NODE-NO_BRANCH = 'X'.
*  NODE-EXPANDER = 'X'.
*  NODE-DRAGDROPID
  NODE-TEXT '员工工号如下'.
  APPEND NODE TO NODE_TABLE.
**設置子節點屬性
  SORT IT_TREE BY USERNO.
  L_INDEX 0.
  LOOP AT IT_TREE ASSIGNING <FS1>.
    CLEARNODE-N_IMAGE.
    L_INDEX L_INDEX + 1.
    NODE-NODE_KEY L_INDEX  .
    NODE-RELATKEY 'ZOA_REG'.
    NODE-RELATSHIP CL_GUI_SIMPLE_TREE=>RELAT_LAST_CHILD.
    NODE-HIDDEN ''.        " THE NODE IS VISIBLE,
    NODE-DISABLED ''.
    NODE-ISFOLDER ' '" 'X' 表示可折叠;'':表示不可折叠
    NODE-TEXT <FS1>-USERNO.
    APPEND NODE TO NODE_TABLE.
  ENDLOOP.
ENDFORM.                    " BUILD_NODE_TABLE


*&---------------------------------------------------------------------*
*&      FORM  GET_DATA
*&---------------------------------------------------------------------*
*       TEXT
*----------------------------------------------------------------------*
*      -->P_R_DATE  TEXT
*----------------------------------------------------------------------*
FORM GET_DATA .
   CLEAR IT_TREE[].
  SELECT FROM ZOA_REG
  INTO CORRESPONDING FIELDS OF TABLE IT_TREE WHERE WERKS '1201'.
   IF  IT_TREE[] IS NOT INITIAL.
   ELSE.
    MESSAGE 'ERROR' TYPE 'E'.
  ENDIF.
ENDFORM.                    " GET_DATA

5、画面SE51中设置如下图:

6、数据表如下:

7、运行结果:

 

至此,树控件设置结束。

posted @ 2011-12-11 15:09 梦心 阅读(33) 评论(0) 编辑

    内文这个东西就像长篇大论的描述,跟在WORD里面一样可以输入无数个字。如果在sap中输入事务码进去可以维护多少个字都可以,但是如果是用.NET调用BAPI传进去内文的话,会将内文超过132个字符的内容全部截掉。要怎么样才能全部导入所有内文呢?

    以下以导入请购单的程序来描述:   

//表头内文 转换多笔实现超长文本插入

......
PRHEADERTEXT.Insert();                             // 这个是BAPI里面导入内文的参数
if (txtQG07.Text.Trim().Length > 120)          // txtQG07.Text的内容就是所有内文,这里用120个字符判断
{
int len = txtQG07.Text.Trim().Length / 120;  // 这里len取整数
len = len + 1;                                             // 可能有结余,故加一
for (int q = 0; q < len; q++)
{
if (q + 1 == len)                                        // 如果到了最后一行,则去最后一行之后的所有内文
PRHEADERTEXT.CurrentRow.SetValue("TEXT_LINE", txtQG07.Text.Trim().Substring(q * 120));
else
PRHEADERTEXT.CurrentRow.SetValue("TEXT_LINE", txtQG07.Text.Trim().Substring(q * 120, 120));
PRHEADERTEXT.Append();                         // 将截取的每一段内容都附加到内文后面。如果不加这一句,则文本会倒置
}
}
else
{
PRHEADERTEXT.CurrentRow.SetValue("TEXT_LINE", txtQG07.Text.Trim());
}

......

如此则可以将内文全部导入。但如果是项目中的内文呢?因为会涉及到传入项目序号,所以需要稍作处理:

//项目内文
PRITEMTEXT.Insert();
if (ds.Tables[0].Rows[i]["QGA11"].ToString().Trim().Length > 120)
{
int len = ds.Tables[0].Rows[i]["QGA11"].ToString().Trim().Length / 120;
len = len + 1;
for (int q = 0; q < len; q++)
{
PRITEMTEXT.CurrentRow.SetValue("PREQ_ITEM", j.ToString());    // 将额外需要的参数都添加到这里,每循环一次都要SetValue一次
if (q + 1 == len)
PRITEMTEXT.CurrentRow.SetValue("TEXT_LINE", ds.Tables[0].Rows[i]["QGA11"].ToString().Trim().Substring(q * 120));
else
PRITEMTEXT.CurrentRow.SetValue("TEXT_LINE", ds.Tables[0].Rows[i]["QGA11"].ToString().Trim().Substring(q * 120, 120));
PRITEMTEXT.Append();
}
}
else
{
PRITEMTEXT.CurrentRow.SetValue("PREQ_ITEM", j.ToString());
PRITEMTEXT.CurrentRow.SetValue("TEXT_LINE", ds.Tables[0].Rows[i]["QGA11"].ToString());
}

 


posted @ 2011-11-12 01:12 梦心 阅读(243) 评论(0) 编辑
摘要: 本周开始,昆山分公司要开始实施SAP了,计划实施周期还是6个月,不再由顾问主导,而是让厦门分公司及台湾总部来规划实施。前期先让两边IT的ERP维护人员把流程整理出来,然后规划出一个方案,按日期整理出来步骤,职责分明。 今天老大就在跟昆山分公司的IT头头通电话,把主导SAP的权力给拿过来,估计以后她有的忙了。不过忙也有忙的好处,可以学到更多,不至于出去什么都不懂。而小苗和小莫则开始了长期的“打杂”,又要跟年初一样干一些“杂活”,貌似她们挺有怨言。一切都要按照老大的安排来,不得松懈。估计要不了多久老大就要去昆山出差了,而ERP维护人员也是紧随其后出差。现在沟通挺重要的,特别是SAP实施阶段,...阅读全文
posted @ 2011-11-08 23:56 梦心 阅读(183) 评论(2) 编辑
摘要: 为什么.net调用SAP的BAPI接口需要调用BAPI_TRANSACTION_COMMIT呢?首先得明白BAPI_TRANSACTION_COMMIT这个BAPI的作用。它功劳很大,在SAP里面很多的BAPI直接调用是不会有结果的,因为需要COMMIT一下才能生效,比如生成资产编号的BAPI:BAPI_FIXEDASSET_CREATE1,如果对他直接在SE37中调用运行或者使用SE38调用它,虽然可以得到一个资产编号,但是在AS03里面查询,系统会很白痴得提示你:该资产编号不存在于XX公司。更搞的是当你在AS01中新建资产编号时,新建的资产编号会跳过之前用BAPI生成“失败”的号码。 ..阅读全文
posted @ 2011-11-01 23:46 梦心 阅读(255) 评论(1) 编辑
摘要: 当所有一切代码准备就绪之后,如果是ASP.NET那就是要发布网站到服务器了。如果服务器上的系统是WIN2003,那很不幸,系统会提示这样的“红脸”过来:意思是说sapnco_utils.dll和sapnco.dll这两个文件不能载入。Could not load file or assembly "sapnco_utils,Version=3.0.0.42,...网上查找了方法也不尽然,各说纷纭。但是在WIN2008下的IIS7跟WinXP下的IIS5.1都可以完全正常,但是这个WIN2003就不行。后来在对这两个DLL进行分析的时候发现它们是用VC++2005开发的,想到WIN20阅读全文
posted @ 2011-10-24 23:22 梦心 阅读(300) 评论(0) 编辑
摘要: 前几天上网无意中看到一条消息说SAP GUI代码编写器跟VS.NET比跟上古时代没啥差别。很多人是从.NET或者JAVA转过来做SAP的,一接触到SAP GUI的代码编写器总觉得非常不满意,但其实实用的多了会发现它在某些方面要比VS.NET优秀的多了。且不说那个双击浏览,单单那个“模式”自动载入函数的功能就让人兴奋不已。省去了很多的麻烦,同时在设置短点方面很人性化,而且Debug的时候还能看到内表的内容出来,真的很方便。 双击一个方法可以自动调出这个方法的主体,如果是INClude,则可以跳入这个程序里面对应的方法体,看完之后则点击“返回”按钮则可以回到上一步操作的地方。貌似这个功能在V...阅读全文
posted @ 2011-09-19 23:36 梦心 阅读(253) 评论(5) 编辑
摘要: 接上篇博文。 上篇博文讲到C#调用BAPIBAPI_USER_CREATE来生成SAP账户,但是新建的账户一点权限也没有,现在我们就再次利用BAPI给账户授予权限。 首先,我们在BAPI画面里查找出相关的BAPI出来,利用角色去给用户添加权限: 双击此BAPI,进入到BAPI预览画面: Table属性页: 第一个ACTIVITYGROUPS是指我们要传进去的表格内容,不再是一个栏位和结构内容了。第二个的RETURN则是返回调用结果。 双击BAPIAGR的结构,进入结构详情画面: 以上AGR_NAME是角色名,应该是必须要的。 那么,接下去就是在C#中编写相应...阅读全文
posted @ 2011-08-28 01:09 梦心 阅读(440) 评论(6) 编辑