公布TstringGrid增强控件TcbStrGrid源码带CheckBox的TStringGr

2016-02-19 12:38 19 1 收藏

今天图老师小编要向大家分享个公布TstringGrid增强控件TcbStrGrid源码带CheckBox的TStringGr教程,过程简单易学,相信聪明的你一定能轻松get!

【 tulaoshi.com - 编程语言 】

unit CbStrGrid;
      {************************扩展的TStringGrid控件TcbStrGrid********************
      [功能简介] 增强的字符串表格控件,主要功能有
          1.在strGrid上显示带CheckBox的列;
          2.设置列标题及列数据对齐方式,列数据的显示方式,如按货币的方式,数字的方式;
            若是按货币/数字方式显示的话,能进行输入控制,即只能输入数字。
          3.自动生成行号,设置要显示合计的行,自动求合计;
          4.加入清除表格clear方法等
      [实现思想]
          1.重载DrawCell方法。按照属性的设置情况,自定义画出显示的内容。
          而实际的值保持不变。
          2.重载SelectCell方法实现设置只读列等。
          3.重载SizeChanged方法实现自动添加行号
          4.根据上面的方法其实你可以做得更多,包括
            在表格中画图片,进度条等
            绑定数据集,相信会对做三层很有帮助。
      [关键属性/方法]
         集合字符串,特指以数字和,构成的字符串,如 '1,2,3'
         1.procedure clear;             //清空表格中的数据
  
         2.procedure DoSumAll;          //对所有的数字列/货币求和
           property OnSumValueChanged: TSumValueChanged
           合计值发生变化时触发
           property DisplaySumRow: Boolean
         是否要显示合计,要显示合计,则用户在strGrid上编辑时,自动更新合计值,若要手动更新合计,
         请调用doSumAll方法
  
         3.property CheckColumnIndex:integer       //设置带checkBox的列
           property OnCheckChanged: TCheckChanged
         当鼠标/空格键操作导致checkBox列的值发生变化时触发该事件
         注意: 只是响应了鼠标/键盘在strGrid上操作,当在程序中赋值而导致的checkbox变化时,该事件并不触发
          function  NonChecked: boolean;   //若没有check选择任何行返回True;
  
         4.property TitleAlign: TTitleAlign     //标题对齐方式
  
         5.property ColsCurrency: String        //以货币方式显示的列的集合字符串
           property ColsNumber: String          //以数字方式显示的列的集合字符串
           property ColsAlignLeft: String       //向左靠齐显示的列的集合字符串
           property ColsAlignCenter: String     //居中显示的列的集合字符串
           property ColsAlignRight: String      //向右靠齐显示的列的集合字符串
           注意:设置时请不要重复设置列,包括checkColumnIndex,为什么呢? 请看源代码
  
         6.property ColsReadOnly: string        //设置只读的列的集合字符串,其他的列可以直接编辑
      [注意事项]
         按方向键有点画FocusRect时有点小问题。
      [修改日志]
         作者: majorsoft(杨美忠)      创建日期: 2004-6-6     修改日期 2004-6-8     Ver0.92
         Email: majorcompu@163.com    QQ:122646527   (dfw)  欢迎指教!
      [版权声明]  Ver0.92
        该程序版权为majorsoft(杨美忠)所有,你可以免费地使用、修改、转载,不过请附带上本段注释,
        请尊重别人的劳动成果,谢谢。
      ****************************************************************************}
  interface
  
  uses
    Windows, SysUtils, Classes, Controls, Grids, Graphics;
  
  const
    STRSUM='合计';
  
  type
    TTitleAlign=(taLeft, taCenter, taRight);  //标题对齐方式
    TInteger=set of 0..254;
    TCheckChanged = procedure (Sender: TObject; ARow: Longint) of object;
    TSumValueChanged = procedure (Sender: TObject) of object;
  
    TCbStrGrid = class(TStringGrid)
    private
      fCheckColumnIndex: integer;
      FDownColor: TColor;
      fIsDown: Boolean;                                 //鼠标(或键盘)是否按下 用来显示动画效果
      fTitleAlign: TTitleAlign;                         //标题对齐方式
  
      FAlignLeftCols: String;
      FAlignLeftSet: TInteger;
      FAlignRightCols: String;
      FAlignRightSet: TInteger;
      FAlignCenterCols: String;
      FAlignCenterSet: TInteger;
      fCurrCols: string;                                //需要以货币方式显示的列的字符串,以','分隔
      fCurrColsSet: TInteger;                           //需要以货币方式显示的列的序号的集合
      fNumCols: string;                                 //需要以数字方式显示的列的字符串,以','分隔
      fNumColsSet: TInteger;                            //需要以数字方式显示的列的序号的集合
      FColsReadOnly: string;                            //只读列的列序号字符串
      FReadOnlySet: TInteger;                           //只读列的序号的集合
      FCheckChanged: TCheckChanged;                     //最近check变化事件
      FDisplaySumRow: Boolean;
      FOnSumValueChanged: TSumValueChanged;                          
      procedure AlterCheckColValue;                     //交替更换带checkbox的列的值
      procedure SetAlignLeftCols(const Value: String);
      procedure SetAlignCenterCols(const Value: String);
      procedure SetAlignRightCols(const Value: String);
      procedure setCheckColumnIndex(const value:integer);
      procedure SetColorDown(const value: TColor);
      procedure setTitleAlign(const value: TTitleAlign);
      procedure setCurrCols(const value: string);
      procedure setNumCols(const value: string);
      procedure SetColsReadOnly(const Value: string);
      procedure SetDisplaySumRow(const Value: Boolean);
      procedure SetOnSumValueChanged(const Value: TSumValueChanged);
    protected
      procedure DrawCell(ACol, ARow: Longint; ARect: TRect;
        AState: TGridDrawState); override;   //画
      procedure KeyDown(var Key: Word; Shift: TShiftState); override;
      procedure KeyPress(var Key: Char); override;
      procedure KeyUp(var Key: Word; Shift: TShiftState); override;
      procedure MouseDown(Button: TMouseButton; Shift: TShiftState;
        X, Y: Integer); override;
      procedure MouseUp(Button: TMouseButton; Shift: TShiftState;
        X, Y: Integer); override;
      function SelectCell(ACol, ARow: Longint): Boolean; override;
      procedure SizeChanged(OldColCount, OldRowCount: Longint); override;
    public
      constructor Create(AOwner: TComponent); override;
      destructor Destroy; override;
      procedure clear;                 //清空表格中的数据
      procedure DoSumAll;              //对所有的数字列/货币求和
      function  NonChecked: boolean;   //若没有check选择任何行返回True;
    published
      property CheckColumnIndex:integer read FCheckColumnIndex write SetCheckColumnIndex default 1;   //设置带checkBox的列
      property ColorDown: TColor read FDownColor write SetColorDown default $00C5D6D9;
      property TitleAlign: TTitleAlign read fTitleAlign write setTitleAlign default taLeft;  //标题对齐方式
      property ColsCurrency: String read fCurrCols write setCurrCols;                        //以货币方式显示的列的集合字符串
      property ColsNumber: String read fNumCols write SetNumCols;                            //以数字方式显示的列的集合字符串
      property ColsAlignLeft: String read FAlignLeftCols write SetAlignLeftCols;             //向左靠齐显示的列的集合字符串
      property ColsAlignCenter: String read FAlignCenterCols write SetAlignCenterCols;       //居中显示的列的集合字符串
      property ColsAlignRight: String read FAlignRightCols write SetAlignRightCols;          //向右靠齐显示的列的集合字符串
      property ColsReadOnly: string read FColsReadOnly write SetColsReadOnly;                //设置只读的列的集合字符串,其他的列可以直接编辑
      {property DisplaySumRow:
       是否要显示合计,要显示合计,则用户在strGrid上编辑时,自动更新合计值,若要手动更新合计,
       请调用doSumAll方法}
      property DisplaySumRow: Boolean read FDisplaySumRow write SetDisplaySumRow;
      {property OnCheckChanged:
      当鼠标/空格键操作导致checkBox列的值发生变化时触发该事件
      注意: 只是响应了鼠标/键盘在strGrid上操作,当在程序中赋值而导致的checkbox变化时,该事件并不触发}
      property OnCheckChanged: TCheckChanged  read FCheckChanged write FCheckChanged;
      property OnSumValueChanged: TSumValueChanged read FOnSumValueChanged write SetOnSumValueChanged;
  
    end;
  
  procedure Register;
  function MyStrToint(Value:string):integer;
  function MyStrToFloat(str:string):extended;
  function PointInRect(const pt:Tpoint; const Rect: TRect):boolean;
  function ExtractNumToSet(const str: string; var aSet: TInteger):Boolean; //从 str中提取数字放到aSet集合中,若成功则返回true
  
  implementation
  
  function MyStrToint(value:string):integer;
  begin
    tryStrToInt(trim(value),result);
  end;
  
  function MyStrToFloat(str:string):extended;
  begin
    if trim(str)='' then
      result:=0.0
    else TryStrTofloat(trim(str),result);
  end;
  
  function PointInRect(const pt:Tpoint; const Rect: TRect):boolean;
  begin
    if (Pt.X=Rect.Left) and (Pt.X=Rect.Right) and
       (Pt.Y= Rect.Top) and (Pt.Y=Rect.Bottom) then
      result:=True
    else result:=false;
  end;
  
  function ExtractNumToSet(const str: string; var aSet: TInteger):Boolean;
  var
    tmpStr:string;
    iComma, i:Integer;  //逗号位置
  begin
    aSet:=[]; //初始化集合
  
    if Length(str)=0 then
    begin
      result:=true;
      exit;
    end;
  
    if not (str[1] in ['0'..'9']) then  //检查合法性1
    begin
      result:=false;
      exit;
    end;
  
    for i:=1 to Length(str) do      //检查合法性2
      if not (str[i] in ['0'..'9', ',']) then
      begin
        result:=false;
        exit;
      end;
  
    tmpStr:=Trim(Str);
    while length(tmpStr)0 do
    begin
      iComma:=pos(',', tmpStr);
      if (tmpstr[1] in ['0'..'9']) then
        if (iComma0) then
        begin
          include(aSet, StrToInt(Copy(tmpStr, 1, iComma-1)));
          tmpStr:=copy(tmpStr, iComma+1, length(tmpStr)-iComma);
        end
        else begin
          include(aSet, StrToInt(tmpStr));
          tmpStr:='';
        end
      else tmpStr:=copy(tmpStr, iComma+1, length(tmpStr)-iComma);
    end;
  
    result:=true;
  end;
  
  procedure Register;
  begin
    RegisterComponents('MA', [TCbStrGrid]);
  end;
  
  { TCbStrGrid }
  
  procedure TCbStrGrid.AlterCheckColValue;
  begin
    if (Row0) and (col=fCheckColumnIndex) then
    begin
      if MyStrToint(Cells[col,Row])=0 then
        Cells[col, Row]:='1'
      else Cells[col, Row]:='0';
  
    end;
  end;
  
  constructor TCbStrGrid.Create(AOwner: TComponent);
  begin
    inherited;
    Options:=Options + [goColSizing];
    fCheckColumnIndex:=1;
    FDownColor:=$00C5D6D9;
    Height:=150;
    Width:=350;
    col:=ColCount-1;
  end;
  
  destructor TCbStrGrid.Destroy;
  begin
  
    inherited;
  end;
  
  procedure TCbStrGrid.DrawCell(ACol, ARow: Integer; ARect: TRect;
    AState: TGridDrawState);
  var
    area, CheckboxRect: TRect;
    CurPt: TPoint;
    value, OffSetX, OffSetY:integer;
    strCell: String;
  begin
    Area:= ARect;
    InflateRect(Area, -2, -2);  //缩小区域  主要作为text out区域
  
    if (ARow0) then
    begin
      if aCol in fNumColsSet then    //数字方式
      begin
        strCell:=FormatFloat('#,##0.##', MyStrToFloat(Cells[ACol, ARow]));
        DrawText(canvas.Handle, PChar(strCell), Length(strCell), Area, DT_RIGHT) //设为靠右
      end
      else if aCol in fCurrColsSet then  //货币方式
      begin
        strCell:='¥'+FormatFloat('#,###.00', MyStrToFloat(Cells[ACol, ARow]));
        DrawText(canvas.Handle, PChar(strCell), Length(strCell), Area, DT_RIGHT) //设为靠右
      end
      else if aCol in FAlignLeftSet then
         DrawText(Canvas.Handle, PChar(Cells[ACol, ARow]),Length(Cells[ACol, ARow]), Area, DT_Left)
      else if aCol in FAlignCenterSet then
         DrawText(Canvas.Handle, PChar(Cells[ACol, ARow]),Length(Cells[ACol, ARow]), Area, DT_Center)
      else if aCol in FAlignRightSet then
         DrawText(Canvas.Handle, PChar(Cells[ACol, ARow]),Length(Cells[ACol, ARow]), Area, DT_Right)
      else if (aCol=fCheckColumnIndex) then    //checkBox方式
      begin
        if (Cells[0, ARow]=STRSUM) then exit;  //合计行的checkBox不画
  
        value:=MyStrToint(Cells[fCheckColumnIndex,aRow]);
  
        Canvas.FillRect(ARect);
        with ARect do
        begin
          OffSetX:=(Right- Left- 10) div 2;
          OffSetY:=(Bottom- Top- 10) div 2;
        end;
  
        CheckboxRect:=Rect(ARect.Left+OffSetX, ARect.Top + OffSetY,     //取得checkBox要画的区域
                           ARect.Left+OffSetX+11, ARect.Top + OffSetY +11);
  
        canvas.pen.style := psSolid;
        canvas.pen.width := 1;
        getCursorPos(CurPt);
        CurPt:=self.ScreenToClient(CurPt);
  
        {画背景}
        if (fisDown) and PointInRect(CurPt, ARect) then
        begin
          canvas.brush.color := fDownColor;
          canvas.pen.color := clBlack;
        end
        else begin
          canvas.brush.color := color;
          canvas.pen.color := clBlack;
        end;
        canvas.FillRect(CheckboxRect);
   
        { 画勾}
        if (value0) then       //不为0表示checked=true;
        begin
          canvas.penpos := Point(CheckboxRect.left+2, CheckboxRect.top+4);//设置起点
          canvas.lineto(CheckboxRect.left+6, CheckboxRect.top+8);         //画到...
          canvas.penpos := Point(CheckboxRect.left+2, CheckboxRect.top+5);
          canvas.lineto(CheckboxRect.left+5, CheckboxRect.top+8);
          canvas.penpos := Point(CheckboxRect.left+2, CheckboxRect.top+6);
          canvas.lineto(CheckboxRect.left+5, CheckboxRect.top+9);
          canvas.penpos := Point(CheckboxRect.left+8, CheckboxRect.top+2);
          canvas.lineto(CheckboxRect.left+4, CheckboxRect.top+6);
          canvas.penpos := Point(CheckboxRect.left+8, CheckboxRect.top+3);
          canvas.lineto(CheckboxRect.left+4, CheckboxRect.top+7);
          canvas.penpos := Point(CheckboxRect.left+8, CheckboxRect.top+4);
          canvas.lineto(CheckboxRect.left+5, CheckboxRect.top+7);
        end;
        {画边界}
        Area:=CellRect(Col, Row);
        DrawFocusRect(canvas.Handle, Area);   //
        canvas.brush.color :=clBlack;
        canvas.FrameRect(CheckboxRect);
      end
      else inherited DrawCell(ACol, ARow, ARect, AState);
    end
    else if (ARow=0) then
    begin
      Canvas.FillRect(ARect);
      case fTitleAlign of
        taLeft: DrawText(Canvas.Handle, PChar(Cells[ACol, ARow]),Length(Cells[ACol, ARow]), Area, DT_Left);
        taCenter: DrawText(Canvas.Handle, PChar(Cells[ACol, ARow]),Length(Cells[ACol, ARow]), Area, DT_Center);
        taRight: DrawText(Canvas.Handle, PChar(Cells[ACol, ARow]),Length(Cells[ACol, ARow]), Area, DT_Right);
      end;
    end
    else inherited DrawCell(ACol, ARow, ARect, AState);
  end;
  
  procedure TCbStrGrid.KeyDown(var Key: Word; Shift: TShiftState);
  begin
    if (key=vk_space) and (Row0) and (col=fCheckColumnIndex)then
      fIsDown:=True;
    inherited;
  end;
  
  procedure TCbStrGrid.KeyUp(var Key: Word; Shift: TShiftState);
  var
    Area:TRect;
  begin
    if (key=vk_space) and (Row0) and (col=fCheckColumnIndex)then
    begin
      AlterCheckColValue;
      fIsDown:=false;
      if Assigned(FCheckChanged) then FCheckChanged(self, Row);
    end;
  
    inherited;
    if key=vk_Up then     //vk_up TMD变态
    begin
      Area:=self.CellRect(Col, Row);
      DrawFocusRect(canvas.Handle, Area);
    end;
  
    if FDisplaySumRow then DoSumAll;
  end;
  
  procedure TCbStrGrid.MouseDown(Button: TMouseButton; Shift: TShiftState; X,
    Y: Integer);
  begin
    if (Row0) and (col=fCheckColumnIndex)then
      fIsDown:=True;
  
    inherited;
  end;
  
  procedure TCbStrGrid.MouseUp(Button: TMouseButton; Shift: TShiftState; X,
    Y: Integer);
  var
    curPt: TPoint;
    Area:TRect;
  begin
    getCursorPos(CurPt);
    CurPt:=self.ScreenToClient(CurPt);
    Area:=self.CellRect(Col, Row);
    if (Row0) and (col=fCheckColumnIndex) and PointInRect(CurPt, Area) then
    begin
      AlterCheckColValue;
      fIsDown:=false;
      if Assigned(FCheckChanged) then FCheckChanged(self, Row);
    end;
   
    inherited;
   
    if FDisplaySumRow then DoSumAll;
  end;
  
  procedure TCbStrGrid.SetAlignLeftCols(const Value: String);
  begin
    if ExtractNumToSet(Value, fAlignLeftSet) then
      FAlignLeftCols := Value
    else Raise Exception.Create('属性值设置错误, 请用数字和,分隔的方式设置属性');
    InvalidateGrid;
  end;
  
  procedure TCbStrGrid.setCheckColumnIndex(const value: integer);
  begin
    if (valuecolCount) then raise exception.Create('CheckColumnIndex越界');
    fCheckColumnIndex:=Value;
    repaint;
  end;
  
  procedure TCbStrGrid.SetColorDown(const value: TColor);
  begin
    fDownColor:=value;
    InvalidateCell(fCheckColumnIndex, row);
  end;
  
  procedure TCbStrGrid.SetAlignCenterCols(const Value: String);
  begin
    if ExtractNumToSet(Value, FAlignCenterSet) then
       FAlignCenterCols := Value
    else Raise Exception.Create('属性值设置错误, 请用数字和,分隔的方式设置属性');
    InvalidateGrid;
  end;
  
  procedure TCbStrGrid.SetAlignRightCols(const Value: String);
  begin
    if ExtractNumToSet(Value, FAlignRightSet) then
       FAlignRightCols := Value
    else Raise Exception.Create('属性值设置错误, 请用数字和,分隔的方式设置属性');
    InvalidateGrid;
  end;
  
  procedure TCbStrGrid.setCurrCols(const value: string);
  begin
    if ExtractNumToSet(Value, fCurrColsSet) then
      fCurrCols:=value
    else Raise Exception.Create('属性值设置错误, 请用数字和,分隔的方式设置属性');
    InvalidateGrid;
  end;
  
  procedure TCbStrGrid.setNumCols(const value: string);
  begin
    if ExtractNumToSet(Value, fNumColsSet) then
      fNumCols:=value
    else Raise Exception.Create('属性值设置错误, 请用数字和,分隔的方式设置属性');
    InvalidateGrid;
  end;
  
  procedure TCbStrGrid.setTitleAlign(const value: TTitleAlign);
  begin
    if not(value in [taLeft, taCenter, taRight]) then  Raise Exception.Create('属性值设置错误,请在[taLeft, taCenter, taRight]选择');
    fTitleAlign:=value;
    InvalidateGrid;
  end;
  
  function TCbStrGrid.SelectCell(ACol, ARow: Integer): Boolean;
  begin
    if (ACol=fCheckColumnIndex) or (ACol in FReadOnlySet) then
      Options:=Options - [goEditing]
    else Options:=Options + [goEditing];
  
    Inherited SelectCell(ACol, ARow);
  end;
  
  procedure TCbStrGrid.SetColsReadOnly(const Value: string);
  begin
    if ExtractNumToSet(Value,FReadOnlySet) then
      FColsReadOnly := Value
    else Raise Exception.Create('属性值设置错误, 请用数字和,分隔的方式设置属性');
    InvalidateGrid;
  end;
  
  procedure TCbStrGrid.clear;
  var
    i,j:integer;
  begin
    for i:=1 to RowCount-1 do
      for j:=1 to ColCount-1 do
       Cells[j,i]:='';         //注意j,i的顺序
  
    InvalidateGrid;
  end;
  
  procedure TCbStrGrid.SizeChanged(OldColCount, OldRowCount: Integer);
  var
    i:integer;
  begin
    inherited;
    for i:=1 to RowCount-1 do
       Cells[0,i]:=inttostr(i);
  
    if FDisplaySumRow then cells[0, RowCount-1]:=STRSUM;
    InvalidateGrid;
  end;
  
  procedure TCbStrGrid.SetDisplaySumRow(const Value: Boolean);
  begin
    FDisplaySumRow := Value;
    RowCount:=RowCount+1;      //仅做刷新用  会调用SizeChanged
    RowCount:=RowCount-1;      //非常规做法。没想到好办法。
    if FDisplaySumRow then DoSumAll;
    InvalidateGrid;
  end;
  
  procedure TCbStrGrid.DoSumAll;
  var
    i, j:integer;
  begin
    if not fDisplaySumRow then exit;
  
    for j:=1 to ColCount-1 do  //先初始化
      if (j in fCurrColsSet) or (j in fNumColsSet) then
      Cells[j, RowCount-1]:='0';
  
    for i:=1 to RowCount-2 do
      for j:=1 to ColCount-1 do
        if (j in fCurrColsSet) or (j in fNumColsSet) then
        Cells[j, RowCount-1]:=FloatToStr((MyStrToFloat(Cells[j, RowCount-1]) + MyStrToFloat(Cells[j, i])));
  
    if Assigned(FOnSumValueChanged) then FOnSumValueChanged(self);
  end;
  
  procedure TCbStrGrid.KeyPress(var Key: Char);
  begin
    if (Col in fCurrColsSet+ fNumColsSet) then
      if not(key in ['0'..'9', '.', '-', char(VK_back), char(VK_Delete)]) then
      key:=#0;
    inherited KeyPress(Key);
  end;
  
  function TCbStrGrid.NonChecked: boolean;
  var
    i, iMax:integer;
  begin
    result:=True;
  
    if FDisplaySumRow then IMax:= RowCount-2 else IMax:= RowCount-1;
    for i:=1 to iMax do
    begin
      if Cells[CheckColumnIndex, i]='1' then
      begin
        result:=false;
        exit;
      end
    end;
  end;
  
  procedure TCbStrGrid.SetOnSumValueChanged(const Value: TSumValueChanged);
  begin
    FOnSumValueChanged := Value;
  end;
  
  end.    

来源:http://www.tulaoshi.com/n/20160219/1601358.html

延伸阅读
  calendar.js    function atCalendarControl(){   var calendar=this;   this.calendarPad=null;   this.prevMonth=null;   this.nextMonth=null;   this.prevYear=null;   this.nextYear=null;   this.goToday=null;   this.calendarClose=null;   this.calendarAbout...
   strAbout+="今日\t: 返回当天日期\n";    strAbout+="\t: 下一月\n";    strAbout+="<<\t: 下一年\n";    strAbout+="\nAgetimeCalendar\nVersion:v1.0\nDesigned By:暂停打印 2004-03-16  afos_koo@hotmail.com \n";    alert(strAbout);   }   calendar.setup(...
    menupad=new Array();     menupad[0]=new Array("替换");     menupad=new Array("--");     menupad=new Array("无间道");     menupad=new Array("无孔不入");     var menu=new atMenu(menubar,menupad);    }   </script   </HEA...
   var tbBoard=document.createElement("table");    divBoard.insertAdjacentElement("beforeEnd",tbBoard);    tbBoard.style.cssText="position:absolute;top:0;left:0;width:100%;height:10;font-size:9pt;";    tbBoard.cellPadding=0;    tbBoard.cellSpacing=1;    tbBoar...
  }    trRow = tbBoard.insertRow(2);    var cnDateName = new Array("周日","周一","周二","周三","周四","周五","周六");    for (var i = 0; i < 7; i++) {     tbCell=trRow.insertCell(i)     tbCell.innerText=cnDateName[i];     tbCell.align...

经验教程

423

收藏

12
微博分享 QQ分享 QQ空间 手机页面 收藏网站 回到头部