找回密码
 注册
搜索
查看: 7252|回复: 0

CRichEditCtrl 超文本编辑(MSN/QQ常用控件)

[复制链接]
发表于 2006-12-11 14:36:01 | 显示全部楼层 |阅读模式
一.常见问题% \. E0 y# S  n) b
a.可以编译,不能执行的
3 e! T/ o6 |, s6 H6 f+ ~' qAfxInitRichEdit();
# f% [: y: I$ a! [/ Y& G
! b% Z3 I& Y# i+ r7 r; Z" m2 [: Xb.升级默认的Riched版本(默认的有一些bug),如1 k$ Z2 o5 _/ j% z& ]4 m
可在InitInstance中添加5 w, g- B. x5 q
LoadLibrary("RICHED20.DLL")   i/ x" W6 O7 s% W
最后注意 FreeLibrary
, o$ \# t# z( c5 {& E: ], C6 o: ?- H, \8 y# F
如果是CRichEditView基类的可用. |" J4 h( K3 V; b
BOOL CXXXXXXView::PreCreateWindow(CREATESTRUCT& cs)
$ j/ N$ E4 g5 Y& _  |: D; i% f) w{
6 m2 e7 m; H2 Y2 ]. ]" H//装入rich edit version 2.01 c7 D  [9 T$ S; N/ b0 H+ X7 c7 o: D
if (LoadLibraryA("RICHED20.DLL") == NULL)
  r. ]" ]% j% M% f; v% z, V+ R{7 @6 J" I5 {3 V* i
AfxMessageBox(_T("Fail to load \"riched20.dll\"."),MB_OK | MB_ICONERROR);
) T- o' e0 j! s6 fPostMessage(WM_QUIT,0,0);
* {1 M$ ~5 c9 c, g: T' V( Ureturn FALSE;$ q( L2 \: h7 W$ [6 |8 H/ d
}% W; x( ~2 ^. b0 z7 T) E* R5 {8 b

! n, T* {6 T' F/ X# _m_strClass = RICHEDIT_CLASSA;//for 2.0 class
& D8 [- ]3 N' B# W# G) {4 r! q) v0 F& ]
return CRichEditView::PreCreateWindow(cs);
, I+ H9 C4 U5 A0 p' z- X2 d}7 S/ K0 t+ ~0 B, V

* K) V8 b+ i) I1 sc.最后追加行
7 H& H: I+ \+ w9 qricheditctrl.SetSel(-1, -1);# T+ M8 I7 C& u
richeditctrl.ReplaceSel( (LPCTSTR)str );
) M. v3 h- U1 Y4 z1 O/ ]; P+ x# a$ |8 G4 z2 E
d.字数限制
) u$ l* u# ]; V3 x: ~CRichEditCtrl::LimitText(long nChars)& n( {& r8 h  x0 t3 L" \

# _) O9 U. M( le.换行切换
; C' C  X. F9 I6 B, `, ~* ]( w5 `CRichEditView的OnInitialUpdate()函数中加入下面两句:
# t1 _. N3 }. q( b1 A8 Y$ D" B% Km_nWordWrap = WrapNone;
) ^. \; _' V0 n5 CWrapChanged();
8 D  E, U+ `/ }3 gWrapChanged实际上也是调用1 J( C' M6 ^. L8 T: {) k3 a
ctrl.SetTargetDevice(NULL, 1); //m_nWordWrap == WrapNone- p: v$ A! J. a; q3 H3 S5 k
ctrl.SetTargetDevice(NULL, 0); //m_nWordWrap == WrapToWindow4 ~9 x1 C7 ]; v- l/ M$ o+ N. A
还有不常用的 m_nWordWrap == WrapToTargetDevice4 u1 F; |7 C& r7 ?4 p
ctrl.SetTargetDevice(m_dcTarget, GetPrintWidth());
  v1 a7 Y6 p0 r7 d: y, B) }0 ?如果是在Dialog中,可使用SetTargetDevice,注意在属性里面加上want return
6 b: x, f$ U, j
- B/ L. i" O4 _" c% u. h# mf.有时候不希望带格式的数据粘贴,可通过PasteSpecial选择性粘贴% J: ?: g! S: ?" Q* ?6 `! R6 ?
pmyRichEditCtrl->PasteSpecial(CF_TEXT);+ T7 Q) `" H/ r( B
& i) ]' b, s$ B: H# I4 V5 F; d# |
g.随着输入随着自动滚动条滚动到最后一行
* J7 ~2 R4 X! |9 n$ a5 v& @int nFirstVisible = pmyRichEditCtrl->GetFirstVisibleLine();
9 f% z5 @( @- P6 h+ Oif (nFirstVisible > 0)
3 q8 z* ?+ \- X: c& x7 ^0 `{
; o/ G3 p; j" G7 @' {# g) m   pmyRichEditCtrl->LineScroll(-nFirstVisible, 0);! n0 o6 H  `# l) W
}+ Y/ L/ g- t* ?

. s$ o& L2 N7 S' M+ A/ X( Cm_cRichEdit.PostMessage(WM_VSCROLL, SB_BOTTOM,0);
$ {3 C0 h$ M% O: e7 u+ O
1 S2 z9 o, b. V  ?* F* c; B0 o
8 M) r" B8 _, M! @h.设置UNDO的次数(只能用在RICHED20以上,即默认不支持,必须升级)
* k2 x" M" @; {0 S, E1 f; b! `SendMessage(EM_SETTEXTMODE,TM_MULTILEVELUNDO,0);1 O# s1 F! Y6 B. v' o4 d
TM_MULTILEVELUNDO 支持多取消(默认值).可通过EM_SETUNDOLIMIT设置最大次数 " I  E$ ~$ j* _, O, F
SendMessage(EM_SETUNDOLIMIT,100,0);
9 u+ J) C! s( p; e. J
; W3 K& o# m- x% i! V! o7 [i.响应OnChange( t6 M$ `2 I/ k
EM_SETEVENTMASK 设置 ENM_CHANGE / Y% Z  R1 i* h, ^. c  ~% m
long lMask = GetEventMask();! ]: _! ^( `9 x, c: A5 Q# h% P7 Q: n1 [
lMask |= ENM_CHANGE;# ~$ W* a  h# f
lMask &= ~ENM_PROTECTED;
, I* j& c2 K6 M% y; oSetEventMask(lMask);& B( {, k- E- }! J. L6 U0 z

  F4 G* C) B% p( u, H9 Z; Dj.设置只读. e2 V- z' g! O' w: U) q" ]8 E+ I
CRichEditCtrl::SetReadOnly( BOOL bReadOnly = TRUE );
; n3 N8 |4 q- ]) h. t2 R+ f通过设置PROTECTED实现选中的文本只读,参见5 T/ |/ s0 X4 Y3 `, ^3 }
http://www.codeguru.com/Cpp/controls/richedit/article.php/c2401/6 J# e1 g% b; G* @' T
* @6 X% y; _0 z$ D6 I
二.函数应用3 B% L3 m5 y, H8 W# j
a.设置字体(主要是通过SetSelectionCharFormat)+ g* y' q1 p2 y2 d* C( q& g2 @
CHARFORMAT cf;- `5 Z, M, K8 c% a5 K
rich.GetSelectionCharFormat(cf);! c- i3 `1 z! h2 I
cf.dwMask|=CFM_BOLD;0 r1 M% c: j  P) \  |8 R
cf.dwEffects|=CFE_BOLD;//设置粗体,取消用cf.dwEffects&=~CFE_BOLD;! A- q  j- v2 o2 k& m( A. G5 O
cf.dwMask|=CFM_ITALIC;* T2 d# @# N1 Z  I
cf.dwEffects|=CFE_ITALIC;//设置斜体,取消用cf.dwEffects&=~CFE_ITALIC;( l$ G7 _! [+ d5 |
cf.dwMask|=CFM_UNDERLINE;
) r! g* G1 z% }: T' g5 W. j# O( Vcf.dwEffects|=CFE_UNDERLINE;//设置斜体,取消用cf.dwEffects&=~CFE_UNDERLINE;  |% }, h2 n3 ]0 S8 g
cf.dwMask|=CFM_COLOR;
5 W2 x" V9 s! c; W( g8 v2 \& kcf.crTextColor = RGB(255,0,0);//设置颜色
2 I" V  j4 ?! r$ ^+ X8 Zcf.dwMask|=CFM_SIZE;
% d$ r9 r6 t( icf.yHeight =200;//设置高度" w# A8 c; M8 q( i7 T7 ]
cf.dwMask|=CFM_FACE;' O1 f- u5 c  c
strcpy(cf.szFaceName ,_T("隶书"));//设置字体( I- @2 S* i2 C/ X
rich.SetSelectionCharFormat(cf);; ^, b5 r) }  X7 N% ^
& u7 S& y0 e& M  _6 R
b.设置字体的行间距
9 h1 N3 Z& t% Q; G- {要用richedit2.0以上0 j! J' q' X  ?: M0 P
试试
# V6 u8 j0 C1 p  U  p# v/ hPARAFORMAT2 pf;7 `' b. A# i7 H) J, F& x, g
pf.cbSize = sizeof(PARAFORMAT2);; w5 N# R( F& g& D. D. ^0 ?% {
pf.dwMask = PFM_NUMBERING | PFM_OFFSET;
+ \; p/ }5 f! J: j  T/ Rpf.wNumbering = PFN_BULLET;//注意PFM_NUMBERING
9 O5 u7 ?  A5 ~3 k7 W. @pf.dxOffset = 10;0 H: t' S: ~# g% Y
VERIFY(SetParaFormat(pf));
# n9 d3 J7 k4 g0 Z( x. P常用的dwMask有
/ n! b. Y2 D* q2 q, _& l* w2 \PFM_NUMBERING 成员 wNumbering 才起作用,项目符号,默认用PFN_BULLET
; y+ z$ J# X6 H! }% i+ X2 使用阿拉伯数字 (1, 2, 3, ...).  ! }: d) ^4 K( w# M6 J; I. r
3 使用小写字母 (a, b, c, ...).  
: K8 F9 ?# m' a, V: U* k: n" {- D4 使用大写字母 (A, B, C, ...).  * ?% N, W6 Q1 t& X
5 使用小写罗马数字 (i, ii, iii, ...).  ; b# f" k1 u& _
6 使用大写罗马数字 (I, II, III, ...).  9 t1 A1 B1 d* {8 S. x$ A) ?6 ?7 A
7 自定义,字符见成员 wNumberingStart.  
* K' p1 K# @; c/ WPFM_OFFSET 成员 dxOffset 才起作用,缩进,单位twips
! G" o: S. W/ ~2 ]PFM_STARTINDENT 成员 dxStartIndent 才起作用,首行缩进  ~4 e/ B& a7 ]/ J1 K
PFM_SPACEAFTER 成员 dySpaceAfter 才起作用,段间距
% R+ w2 D% P( t% wPFM_LINESPACING 成员 dyLineSpacing 才起作用,行间距( ?- `. W' g2 g% a, ?0 k& R
5 l0 A+ _; u9 h
c.设置CRichEditCtrl(2.0)背景透明 % S: l3 J$ s& Y& Q2 k
long style = ::GetWindowLong(GetSafeHwnd(), GWL_EXSTYLE);$ @# U! s; k: C! e3 j$ v1 E
style &= WS_EX_TRANSPARENT;
1 e1 N$ I$ @6 p. n9 t8 Z::SetWindowLong(GetSafeHwnd(), GWL_EXSTYLE, style);0 Y/ R6 V. k2 Y; Z
或 CreateEx,然后把WS_EX_TRANSPARENT样式加上
# s! d; u" T; f. S2 Q4 g, i4 n0 v% Y( N: J, K: l
e.得到内容有三种
( G( }' N3 ?" b* x& g1)GetWindowText; `/ w! x+ s+ L$ X& w
2)使用EM_GETTEXTEX
: `1 D2 T0 ], C- {* N3 sGETTEXTEX gt;
* p4 b( ~: i" V! ?" x  cgt.cb = 200;
3 g8 b3 _; `3 ~! z- l4 Hgt.flags = GT_DEFAULT;
* U, e" e5 x+ N( Sgt.codepage = CP_ACP ;
0 j5 x3 v5 ~8 ?5 n' ?gt.lpDefaultChar = NULL;
7 j) d2 R4 S3 E' O  j6 z- Bgt.lpUsedDefChar = NULL;
/ d2 y( z6 O3 j! S3 Z6 D7 ASendMessage(EM_GETTEXTEX,(WPARAM)&gt,(LPARAM)text);
: c1 S  P! O9 a. j" y8 j  ]3)StreamOut(主要用于RTF等格式输出)
3 t+ |) ~  \# S+ W& \- y- @static DWORD CALLBACK
" Q# i: d% X: z1 z8 T$ yMyStreamOutCallback(DWORD dwCookie, LPBYTE pbBuff, LONG cb, LONG *pcb)/ r0 |+ y3 ]" {) \
{$ n3 U+ k; t9 `0 C1 u
   CFile* pFile = (CFile*) dwCookie;
3 E- e$ U( A; R- X
1 {% z# v/ D' p- Y  E+ Y   pFile->Write(pbBuff, cb);2 J# P2 R6 M; k' p+ N1 W+ A
   *pcb = cb;
2 X0 k$ T. D% W6 A) K( M. G$ n( ]3 P) k
   return 0;
/ _% X& _9 q) l' j$ L5 W" \}, J( _# N& `. ^

0 ~5 G6 l( h9 X- Z
( ]* \( |2 W# A8 Y9 q4 L: ^# P   CFile cFile(TEXT("myfile.rtf"), CFile::modeCreate|CFile::modeWrite);- I8 q7 Z8 t7 \6 @- M* }/ |( v3 O
   EDITSTREAM es;5 {" X7 q0 j+ p- l4 b# x" J' D- N; ]
   es.dwCookie = (DWORD) &cFile;//设置用例参数,以便回调函数调用
1 ^, N6 Y' `8 j+ R( C   es.pfnCallback = MyStreamOutCallback; 3 M! l6 |6 C( B. Z  c1 ^& M$ _
   pmyRichEditCtrl->StreamOut(SF_RTF, es);2 _7 K* `$ A( `. s4 S$ s1 _
读入可以此类推,SetWindowText,EM_SETTEXTEX,StreamIn) o7 z) H$ M' h: V7 u% K8 f0 f

2 _/ P" w3 U. ^) L- h; af.查找字符串
5 F$ h# ~' h9 P/ o: LFINDTEXTEX ft;
! ?3 ~# j6 l8 H. [0 a9 t0 E; jft.chrg.cpMin = 0;" l* [, I7 w' S$ M9 Y3 G% m" u
ft.chrg.cpMax = -1;
3 M0 t+ J" R& v4 Wft.lpstrText = "|";
# `2 P' E' i1 V! S' g  y. ^  slong lPos = FindText(0, &ft);
# D- E0 M# _9 I! m7 j  }9 T( O/ x. \% l* b
如果要继续查找,修改cpMin,如
+ c1 O9 \+ Y3 V) Yint nCount = 0;
* D# ~0 z. [( {' |8 R: Xdo
3 M. x" z4 O7 J. `; _! M9 B{( l) t$ M2 e( ^* h  N
long lPos = GetRichEditCtrl().FindText(0, &ft);
# |( G/ C+ v) g: o- L! ^) {( P! D9 O if( -1 == lPos) break;
1 i+ y( U6 J! e! f ft.chrg.cpMin = lPos + strlen(ft.lpstrText);5 o# F2 l# v4 R3 P0 H- u, W) U
++nCount;  l5 v4 a" d- Y) a3 X6 {: N
}while(TRUE);
" j; j; r5 D8 ~& ?1 U( m, R4 ~- W7 s9 {" F* R. {) B: J0 q, [
g.以Html格式保存
  @9 D0 _* ^3 H5 i- Y目前做法可先转为RTF格式,再通过RTF-to-HTML Converter
+ O# s6 e! |) o- Whttp://www.codeguru.com/Cpp/controls/richedit/conversions/article.php/c5377/
7 h$ p8 n( k3 O1 K
9 }# a9 j% D7 i- D! ?% F( g9 E* V' v8 m8 fh.重载OnProtected函数得到对应的消息,如粘贴等
7 S/ w! C) f# X+ E# b, \1 P! Rvoid CMYichEditorView::OnProtected(NMHDR* pNMHDR, LRESULT* pResult). I6 X8 n1 K/ n9 j
{& D2 e3 t, q9 N- X! |* O
ENPROTECTED* pEP = (ENPROTECTED*)pNMHDR;
) P; n$ h; d' G; j  @8 L% w7 n) _0 M4 }. [
switch (pEP->msg) {
7 }. X* @& i1 H1 @5 {( E case WM_KEYDOWN://按键,判断pEP->wParam' _6 O# U, y% d
case WM_PASTE://粘贴& {, I2 h( g, C: q! E) F- u
case WM_CUT://剪切; |  H% z  A$ i4 v! i: Q
case EM_SETCHARFORMAT:
" X2 ]! ]# T8 l$ W default:5 T6 }0 g( e1 \$ ]
  break;( ^& u) q3 }; ?# |4 j
};
8 O+ R8 \1 |) m; E " a4 r, h8 l+ e& ?+ o
*pResult = FALSE;  [- u6 S6 [: `4 e& L) T
}  o6 j( c/ s/ a9 ^, ?8 d

3 e% r" C* ?- V: P6 l/ Q( E三.聊天常用  
7 y8 s4 t7 Y; ^5 n3 _7 U  Pa.LINK 链接功能- q: p4 P5 [" P; |% R
1.  LoadLibrary(_T("Riched20.dll"));
5 K5 u9 ?+ o3 h. u9 ~' I2. 创建RichEdit2.0控件
: I- g$ R  u2 ]1 T CreateEx(0, _T("RichEdit20A"), NULL, WS_CHILD|WS_VISIBLE|WS_VSCROLL|WS_TABSTOP
# B( g/ D. U6 p|ES_READONLY|ES_WANTRETURN|ES_MULTILINE,
* d  h4 U5 N/ t& Q2 Y. {          rect.left, rect.top, cx, cy,
5 y1 ]# V% `2 o9 K* H6 {- H8 J8 G  pParentWnd->m_hWnd, (HMENU)nID, NULL);1 o! U+ s6 S! ~# x$ n0 l' Y
3. 设定选中的文字为链接显示( [2 a- M  e. ?/ `% l% Y
CHARFORMAT2 cf2;
6 W* }5 o* E* E+ Z! V5 d. p" N6 }: F% nZeroMemory(&cf2, sizeof(CHARFORMAT2));//, B8 Y6 P5 Y2 b( n. \7 ?
cf2.cbSize = sizeof(CHARFORMAT2);
! N+ H8 u, z$ @& \& Ecf2.dwMask = CFM_LINK;9 m" ?# {( b% V8 ^
cf2.dwEffects |= CFE_LINK;
( p9 M' z/ m2 _3 h9 W, G( Km_cRichEdit.SendMessage(EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);) O- ~' s- W( {, ?3 r
4.支持链接的点击响应  Y$ k% O9 n5 u9 C* }
m_cRichEdit.SetEventMask(ENM_LINK);
" e5 u9 E5 T" V/ Z4 P0 W5.响应链接的点击EN_LINK; }  F" G5 G. D7 c

$ v' @( }* }' Z+ s- t! LBEGIN_MESSAGE_MAP(CMyRichEdit, CRichEditCtrl)7 u5 j6 I$ q, U4 R" ?# [5 |' F
ON_NOTIFY_REFLECT(EN_LINK,OnURL)
5 f' _& \% Q! L1 [5 OEND_MESSAGE_MAP()5 Q. `4 X8 B" ^% W* t. i
......* c- G' d* p' I. [# R% _; P: @$ _
4 a; a# p6 G& b
void CMyRichEdit::OnURLClick(NMHDR *pNmhdr, LRESULT *pResult), W# {/ A3 ^; e. z
{& f; n% l. f! o$ e0 L! B
TCHAR LinkChar[512];6 W8 \1 G/ r/ e: j) i6 s( w2 @
ENLINK *pLink = (ENLINK *)pNmhdr;  Z( u: e0 R9 Z! S. d
if (pLink->msg == WM_LBUTTONUP)
2 O1 M+ S$ L9 S- `{
8 V5 o3 j6 J. |" V. ~/ iSetSel(penLink->chrg);//这是链接的文字范围
% H; }7 M6 K7 i/ ulong Res = GetSelText((char *)LinkChar);//这是链接文字
+ i& o( l9 m/ {$ e                  //后面是你的处理过程
' ~1 ^1 d1 P9 C7 ^                  ......
( s7 C! S' g3 ~* j         }" e9 x& B7 B' y& n" |3 y3 A
}
5 j1 l) e. [" p2 P8 I4 v% c0 `
( L) I& {% y% \. R$ ]( \6 mb.插入位图4 g) T+ I  r# _. R/ w7 h
http://www.codeguru.com/Cpp/controls/richedit/article.php/c2417/
4 ]0 R$ s. F+ ^: Shttp://www.codeguru.com/Cpp/controls/richedit/article.php/c5383/
4 h/ M; _; Y; ?* D2 F6 i
9 J5 n8 a/ y) X/ I5 i' R自定义在RichEdit中插入对象的图标
  h1 S# F! i. vhttp://www.blogcn.com/user3/jiangsheng/blog/1319738.html+ [9 j- N% s  T& {& ?
方法基本同Knowledge Base文章Q220844 HOWTO: Insert a Bitmap Into an RTF Document Using the RichEdit Control; W. B# W) g/ r, E9 n: A
只是在最后插入之前调用一下IOleCache::SetData,用一个HGLOBAL作为参数,HGLOBAL里面的数据是一个METAFILEPICT结构,包含自己提供的图片/ `- T* x% ^. r4 }

, U/ e2 m) o) G" t) n/ ~使用CRichEditView::InsertFileAsObject就可以插入图像。VC++带有一个例子WordPad。
4 {6 e/ p3 S: T7 d! ?  d8 Z9 R另外可以参考“Insert any HBITMAP (Bitmap) in your RichEdit Control”(http://www.codeguru.com/richedit/richeditrc.html)。
4 D8 `8 ^) c* ]& g1 f8 @
& z" g* f) ~( j& w* I1 I" U: Kc.显示GIF动画
; a8 j8 V4 Y  w4 K- K常用的是通过qq的imageole.dll(也有用Gif89.dll的): v. |4 i. I9 f( b0 A! L+ F+ q
http://www.xiaozhou.net/cooldog/blogview.asp?logID=82
( E3 `. B" {6 K9 qhttp://www.codeproject.com/richedit/AnimatedEmoticon.asp( d: ~! e# S. r
+ @  B4 \; |( K4 M) A
在richedit控件中插入动态GIF (Native C++版)6 q/ h' a2 y% H
http://blog.joycode.com/jiangsheng/archive/2004/12/15/41209.aspx
- K+ }) ?# N, p
" G+ ~% v. r) @) ld.IRichEditOleCallback的使用3 |  ^- @4 l3 x/ f- }1 D
http://61.186.252.131/Expert/topic/905/905844.xml?temp=.8379022/ R5 n: ]( a( z
" p: R# ?' g) Q& R, B
类似 MSN 信息发送框的制作(上)
: Q4 M5 l0 a( ]! ^  ^/ [4 s. @http://www.vckbase.com/document/viewdoc/?id=10877 O; m' g- v7 _; E# i" B
内容包含:实现右键菜单,图片插入,读取/写入RTF格式字符串
8 D) z4 ~/ E& ~& D
8 T! |6 N" K# ~4 k3 I- }自定义 CRichEditCtrl 控件
2 H4 i3 B5 j4 W- P* Q1 `5 yhttp://www.vckbase.com/document/viewdoc/?id=328
8 r- U" X0 c( q内容包含:鼠标右键消息,消息映射,字体变换
. e6 q9 p8 u( L8 k/ p6 v
' ~* \& {' Q- t/ vPS.richedit控件升级到2.0后,先把字体设为楷体,输入汉字没有问题,但输入字母时,字母自动跳转为Arial字体,而1.0却没有这个文题,仍然是用楷体显示字母
0 @% E) t9 p: N* u* ]- N是一个专门的设计 Dual-font, Smart font apply, 参见 http://61.186.252.131/Expert/topic/913/913807.xml?temp=.3753778 : X$ g* }( z1 v: B* }5 |5 {
----------------------
0 k4 q9 p& ]* c) K& |0 @! y比我想象中还要花时间,所以最后潦草了点,见谅
您需要登录后才可以回帖 登录 | 注册

本版积分规则

Archiver|手机版|小黑屋|宁德市腾云网络科技有限公司 ( 闽ICP备2022007940号-5|闽公网安备 35092202000206号 )

GMT+8, 2026-6-18 12:35 , Processed in 0.020194 second(s), 15 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

快速回复 返回顶部 返回列表