CSDN博客

img Angus_Li

源程序代码规范 Angus_li(收藏)

发表于2003/1/11 10:01:00  1339人阅读


源程序代码规范

I. 总则

1.1    目的
为了对程序员的开发进行适当的规范化,特制定本规范。其根本目的,是为了保证程序具有良好的、一致的结构,以期提高程序的可读性及可维护性,方便程序的测试、维护、升级等工作,同时,也培养程序员书写代码的规范性。

1.2     原则
      名称反映含义,形式反映结构;

1.3 申明
本规范适用于采用DELPHI作开发工具的公司所有项目,程序员应严格按照本规范编写代码,如项目有确实需要的特殊要求,也必须经项目经理审核后,把该特殊要求形成文档当作本文档的随附文件一起保存。

1.4       文档编写  :  薛国顺   2001年6月23日
第一次修改:薛国顺   2001年7月7日
第二次修改:薛国顺   2001年9月28日

II.代码规范

2.1       项目规范

2.1.1. 每一个项目的代码、文档按模块、功能必须在项目文件夹中有条理的归类存放,每个项目文件夹中均必须包含以下子文件夹:
            Code:源代码目录
            Sql:数据库脚本目录
            Demodata:演示数据目录
            Userdata:用户数据目录
            Help:帮助文档目录
            Install:安装文件目录
            Document:文档目录
            Picture:图片目录
            templates:报表模板目录
            子文件夹下也必须依照详细的用途分类建立子目录。详细的示例如下:

示例:

Project Name
|
code     sql     demodata     Picture     help     install     document    templates
|                               |
appserver     client                              |
|                              测试文档  设计文档  数据字典  需求分析  用户文档
module1    module2    module3 ……

2.1.2. 每个项目的主目录下均必须有一个项目说明文件,说明该项目的一些概要性提示和相关规范。

2.1.3. 在项目文件夹下的每层每个子目录中必须有一个文件夹说明文件,说明该层文件夹及其子文件夹的分类方法和含义。任何时候,新增一个文件夹时,均必须在同层目录下的文件夹说明文件中添加所新增文件夹的分类含义,同时创建该文件夹下的文件夹说明文件。

2.1.4. 工程文件代码规范:
A.任何一个工程文件(包括动态链接库工程文件)的第一部分必须用注释的形式说明项目名称、公司版权、工程描述、版本说明、创建日期、作者以及后续更新人员。
B.除主模块、公共函数模块和公共数据模块外,所有该项目下的单元不可由项目自动创建(CREATE),在加入新单元后,必须在工程文件中删除自动CREATE的语句。
C.工程文件的其余代码遵行本文档约定的单元规范及其它代码规范;

2.2       单元总体规范

2.2.1   每个单元的第一部分必须用注释的形式说明项目名称、公司版权;

2.2.2  必须紧接在单元名之后以注释的形式说明项目名称、模块名称、模块描述、模块版本、创建日期、作者、更新人以及TODO列表;

2.2.3  在INTERFACE部分USE的单元原则上只允许DELPHI的IDE自动添加,如确需自己添加必须在引用到的单元名后用{}注释添加原因。用于编译开关控制的伪指令插入在USE之前。

2.2.4   Interface部分应当只包含需要被外部单元访问的类型、变量、过程和函数的声明,而且这些声明应当在Implementation部分之前。

2.2.5    Implementation部分包含本单元私有的类型、变量、过程和函数的声明。

2.2.6   除非确实需要,不要在Initialization部分写过多代码,如有代码应详细注释说明必须写在Initialization部分的原因。

2.2.7   在Finalization部分释放所有在Initialization部分中分配的资源。

2.2.8   除主FORM外,每个FORM单元都应当含有实例化函数(入口函数),用于创建、设置、显示和释放FORM。FORM的变量应当从单元中移走,改在实例化函数中作为局部变量定义(要先从Project Options对话框的自动生成列表中移走该FORM)。(参见2.1.4.B)

2.2.9    总体风格
缩进规则:每级间保持两个空格。不要将"Tab"符号存储到源文件中,原因是"Tab"字符在不同的源代码管理中宽度会有不同的定义。在Tools | Editor Options 的General 页不要选中 Set Tab Character和Optimal Fill复选框,制表符"Tab"即不会被保存。
边距页宽:边距应保持默认设置的80个字符,只要可能,长度超过一行的语句必须用逗号或运算符换行。换行后,应缩进两个字符。
注释规则:只有在一行的行末注释时才用//,否则均必须使用{}来注释。
空行使用:需使用空行的地方有:版权块、类之间、方法过程函数之间、方法内部算法分段。空行只能用一行,多余的空行必须删除。
Begin…End:任何情况下,Begin和End均必须各自独占一行。End总和对应的Begin左对齐。
工具使用:所有单元提交给版本控制工具之前必须用Source Code Formatter格式化过。

示例: 

{*******************************************************}
{                                                                      }
{                     项目名称                              }
{                                                                                     }
{            版权所有 (C) 2000,2001 公司名称                              }
{                                                                                   }
{*******************************************************}

unit UnitName;
{*******************************************************
项目:
模块:
描述:
版本:
日期:
作者:
更新:
TODO:
*******************************************************}

interface

{$I IS.inc}

uses
   ----,----,----,----,----,----,----,----,----,----,----, ----;

const
   --------------------;
   
type
   --------------------;
 
var
   --------------------;

implementation

uses
   ----,----,----,----;

{$R *.RES}
{$R *.DFM}

--------------------------------;
--------------------------------;
--------------------------------;

end.

2.3      单元内部规范

           2.3.1    常量区:
                      A,所有常量的第一个前缀必须为C,常量必须分主题归类定义,如有多个主题,每一个主题必须加一个主题前缀。前缀与前缀之间、前缀与名称之间用_分割。
                      B,每个常量的定义单独一行,在同一行上,必须用//加注释说明该常量的含义。
                      C,按主题归类的常量,在每个主题开始的第一行必须用{}注释主题含义。

                      D,示例:

Const

  { 主题1 }
C_主题前缀1_---    = ----;  // 含义
C_主题前缀1_---    = ----;  // 含义
C_主题前缀1_---    = ----;  // 含义
C_主题前缀1_---    = ----;  // 含义
  { 主题2 }
C_主题前缀2_---    = ----;  // 含义
C_主题前缀2_---    = ----;  // 含义
C_主题前缀2_---    = ----;  // 含义
C_主题前缀2_---    = ----;  // 含义

          2.3.2    类型区:
A,不提供服务的数据类型定义格式为:
T---- = ---------          // 类型含义
B,有状态并提供服务的数据类型定义格式为:
T---- = class(----)     //类型含义
private
  --------
protected
  --------
public
  --------
published
  --------
end;
                     C,原则上,数据类型及其内部方法、属性、数据定义时应按字母顺序排列。属性的读写方法分别以Get和Set为前缀命名。
D,内部数据、属性、方法定义规则:
      D1,Private区
1、所有数据放在Private 区,以F打头。
2、所有事件属性对应的方法指针放在Private 区,以F打头。
3、不准备被继承的属性的Get与Set方法放在Private 区。
4、响应消息的方法放在Private 区。
D2,Protected区
1、被子类调用的但不能被外界调用的方法与属性。
2、供子类重载的方法 virsual;      virsual;  abstract。
D3,Public区
1、构建析构方法
2、供外界调用的方法
3、供外界调用的属性
D4,Published区
1、出现在Object Inspector里供设计时用的属性
2、出现在Object Inspector里供设计时用的事件响应 
                       E,特殊的数据应在定义一行的行末用//注释其含义。特殊的属性、方法                              应在定义前一行用{}注释其含义。事件指针的定义不需注释,但事件类型定义时必须在其前一行用{}注释其含义。

           2.3.3    变量区
  A,在变量区定义单元的全局变量。
  B,每个需要注释的变量单独一行,在行末用//注释其含义。
  C,同一类型且含义逻辑上不并列的变量分开定义,同一类型且含义逻辑上并列的变量在一起定义。
  D,一般不鼓励使用全局变量。在确实需要使用全局变量的时候,必须把全局变量限制在需要的环境内,如:一个全局变量可能只在单元的Implementation部分是全局的,所以必须把其定义在该部分内。被多个单元使用的全局数据应该被移入一个公共模块中。
  E,全局变量可以在定义时直接初始化为某一个值。注意:所有的全局变量将自动进行零初始化,所以,不要将全局变量初始化为空值,如0, nil, '', Unassigned。零初始化的全局变量在可执行文件中不占用空间,非零初始化的全局变量则在可执行文件中占空间。
  F,变量的命名规则参见本文档后面的命名规则项。

           2.3.4    实现区
                        A,事件、过程、函数应按主题分类归集在一起,每个主题开始的第一行用{}注释该类主题。约定归类方法如下:同一个元件的事件应归集在一起,被事件调用或与之相关系的函数或过程应紧跟在该事件之后,函数、过程、方法之间如存在调用关系也应归集在一起,除非该函数、过程被多处调用。被多处调用的单元内部函数、过程应归集在单元的公共过程主题内。可以自定义归类方法,比如按实现功能归类,但如归类方法不同于本约定的,应在所有主题之前用{}注释说明归类方法。
                        B,每一个事件、方法必须在其代码前用{}注释说明名称、描述、参数含义、创建时间、作者、更新人、更新时间。在事件、方法内部不同含义的算法或不同主题的代码间空一行,每段算法或主题的开始用{}注释算法或主题的目的。关键性的算法行应在行末用//注释算法目的。事件、方法内部的变量定义时必须遵行2.3.3约定的变量区规范。
                         C,SQL 语句书写规则:在SQL语句中,所有SQL保留字统一用大写,所有字段名以及数据表名全部用小写,SELECT, FROM, WHERE, ORDER BY 顶格起行,各子句中要求每个字段或每个条件或每个关联单独起行. 并缩进对齐,缩进以两格为准。示例如下:
strMasterSql :=
  ' SELECT preload.*, ' +
  '   codelib.codename vesselname' +
  '   blstatus = CASE blstatusid WHEN “1” THEN “订舱” ' +
  '                              WHEN “2” THEN “配航线” ' +
  '                              ELSE “其他” ' +
  '              END; ' +
  ' FROM preload ' +
  '  LEFT JOIN codelib ' +
  '    ON codelib.codecd = preload.vesselcd ' +
  '      AND codelib.classid = "03" ';
strMasterFilter :=
  ' WHERE preload.companycd = :companycd ' +
  '   AND preload.idno = :idno ';
strMasterOrder := 'ORDER BY preloadno';
注意:CASE...WHEN...THEN...ELSE...END语句的写法不同。
                         D,代码中用到数据库字段名称的地方,统一用小写。
                         E,元件本身的属性、事件名,要按照DELPHI自动提示的格式写。


2.4       Object Pascal 语法约定

            2.4.1    语法杂项:
A,运算符前后要有各一个空格。
                         B,在开始圆括号和后一个字符,以及结束圆括号和上一个字符中不出现空格。不要在语句中出现不必要的括号。如 if (I=42) then 中,括号就是不必要的。
                         C,Object Pascal 语言的保留字和关键字总是小写。
                         D,一般不建议使用Variant和OleVariant,除非数据类型只有在运行期才能确定或获知的。OleVariant常常用来做基于COM的程序:Auutomation和ActiveX控制,Variant基于non-COM的程序。
                         E,代码中不可使用Real类型,对于浮点数,应当使用Double类型。
                         F,定义对象特性时,应当使用读写方法来访问。
                       
            2.4.2     过程与函数
                         A,形参的顺序主要要考虑寄存器调用规则,
最常用的参数应当作为第一个参数,按使用频率依次从左到右排。
输入参数应当位于输出参数之前。
范围大的参数应当放在范围小的参数之前,如SomeProc(Acountry, Astate, ACity)
IDE自动生成的事件句柄,参数顺序不必依此约定。
                        B,所有的常量参数应当标以Const标记。
                        C,为避免命名冲突,除调用公共模块的的例程外,调用其他模块的过程、函数时应当在例程名前加该例程所在的单元名。
                        D,除非确实需要在使用前再初始化,否则例程的内部变量应当在例程的入口处立即初始化。

              2.4.3   语句规范
                         A,IF语句:
在if/then/else语句中,最可能执行的情况应放在then子句中,出现可能较小的情况放在else子句。
尽量避免使用连续的if语句,应该使用case语句来代替。
不要嵌套5层以上的if语句。
不要在if语句中使用多余的圆括号。
如果if语句中存在多个条件判断,条件应该按从最快的到最慢的计算难度依次从左到右排列整齐。
IF语句的规范示例:
if (--------) then
    -------------;

  if (--------) then
  begin
    -------------;
    -------------;
  end;

  if (--------) then
    -------------
  else
    -------------; 

  if (--------) then
  begin
    -------------;
    -------------;
  end
  else
    -------------;

  if (--------) then
  begin
    -------------;
    -------------;
  end
  else
  begin
    -------------;
    -------------;
  end;

  if (--------) then
    -------------
  else
  if (--------) then
    -------------; 
                        B,CASE语句:
CASE语句中,代表每种情况的常量应该按照数字或字母顺序排列。
每种情况所对应的动作语句应当简短且通常不超过4-5行。如果动作太复杂,应将代码放到一个单独的过程或函数中。
CASE语句的ELSE子句只用于默认情况或错误检测。
CASE语句的规范示例:
   case -------- of
     -------- :   -------------;
     -------- :   -------------;
     -------- :   -------------;
           else   -------------;
   end;

   case -------- of
      -------- :
        -----------------------------------------------------------------;
      -------- :
        -----------------------------------------------------------------;
      -------- :
        -----------------------------------------------------------------;
           else
        -----------------------------------------------------------------;
   end;

  case -------- of
     -------- : begin
                        --------------------------;
                        --------------------------;
                        --------------------------;
                end;
     -------- : begin
                        --------------------------;
                        --------------------------;
                        --------------------------;
                end;
     -------- : begin
                       --------------------------;
                       --------------------------;
                       --------------------------;
                 end
            else 
begin
                       -------------;
                       -------------;
                       -------------;
                 end;
  end; 
                         C,WHILE语句:
不要用Exit过程来跳出一个While循环。如果需要的话,应该使用循环条件退出循环。
所有对While循环进行初始化的代码应当位于While入口前,且不要被无关的语句分隔开。
While语句的规范示例:
  while ------ do
  begin
    -------------;
    -------------;
    -------------;
  end;
                        D,FOR语句:
如果循环次数是确定的,应当用FOR语句代替WHILE语句。
FOR语句的规范示例:
  for I := -------- to -------- do
     -------------;

  for I := -------- to -------- do
  begin
      -------------;
      -------------;
     -------------;
  end;

  for I := -------- to -------- do
     if (--------) then
     begin
         -------------;
         -------------;
         -------------;
     end;

  for I := -------- to -------- do
    with -------- then
    begin
       -------------;
       -------------;
       -------------;
    end;
                        E,REPEAT语句:
repeat语句和while语句类似,且遵循同样的规则。
Repeat语句的规范示例:
repeat
   -------------;
   -------------;
   -------------;
until ------; 
                        F,WITH语句:
with语句应该尽量少的使用,尤其是有多个对象的WITH语句。
With语句的规范示例:
   with -------- do
      -------------;

   with -------- do
   begin
      -------------;
      -------------;
      -------------;
   end; 
                       G,try语句:
除非是在单元的initialization/finalization部分或者对象的构造/析构中分配/释放资源,否则,凡是分配资源的地方都必须使用TRY……FINALLY语句来保证资源得到释放。
可能的情况下,每个资源分配应当与一个try……finally结构配对。
不可以在try..exception中使用else字句,因为这样有可能阻碍其他所有的异常发生。
需要在发生异常时执行一些自定义的任务,才使用try……except,如果仅仅是为了显示一个错误信息,没有必要使用try……except,因为Application对象能够自动根据上下文做到这一点。
如果要在except子句中激活默认的异常处理,可以用raise再次触发异常。
Try语句的规范示例:
     try
       -------------;
       -------------;
       -------------;
     finally
       -------------;
       -------------;
       -------------;
     end;

     try
       try
         -------------;
         -------------;
         -------------;
       except
          -------------;
           -------------;
       end;
     finally
       -------------;
       -------------;
       -------------;
     end;

2.5      命名规范

          2.5.1   过程、函数的命名规则。
A,例程名以大写字母开始,且应在具有不同词义单词的第一个字母处用大写。
B,例程名应当有意义,并应尽量使用动宾词组。
C,设置输入参数值的例程名应当以Set为其前缀,获取数值的例程名应当以Get为其前缀。
D,例程的形参名称应当表达出它的用途,只要可能,形参的名称应以字母A为前缀。
E,DELPHI的IDE自动产生的事件句柄,名称和参数不可作改动。

          2.5.2   变量命名规则
A,变量的名称应当能表达出它的用途。尽量不缩写,并使用名词作为变量名。
B,变量名应该用类型的缩写作为前缀,比如strMasterSql。
B,循环控制变量可以是单个字母如I、J、K,也可以使用具有含义的名称。
C,布尔变量的名称必须能清楚的表示出True和False的意义。
E,工程的全局变量应该以G_作为前缀。单元内的全局变量应该以U_作为前缀。

         2.5.3    类型、类、对象命名规则
                     A,定义枚举类型应在名称前加T作为前缀,枚举类型的标识符列表的前缀应包含2~3个小写字符,来彼此关联。如:
                            TSongType = (stRock, stClassical, stCountry, stAlternative, stHeavyMetal);
                            一个枚举类型的变量名命名为类型名字去掉前缀"T",除非由于某个原因给这个变量更有意义的名字。
                     B,数组类型名应表达出该数组的用途,类型名必须加字母T为前缀。如果要声明一个指向数组类型的指针,则必须加字母P为前缀,且应声明在类型声明之前。如:
type PCycleArray = ^TCycleArray;
TCycleArray = array[1..100] of integer;
数组类型的变量命名为类型名去掉前缀"T"。
                     C,记录类型名应表达出该记录的用途,类型名必须加字母T为前缀。如果要声明一个指向记录类型的指针,则必须加字母P为前缀,且应声明在类型声明之前。
                     D,类的名字必须是能够体现这个类的用途。类型名必须以字符"T"开始来表明他是一个类型定义。类型的实例名字通常和类型名相匹配而没有字符"T"。
                      E,类的私有数据域(Fields)命名规则和其他的变量一样,但必须在前面加上字符"F"。
                      F,类的方法命名和过程、函数的命名一样。
                      G,类的属性名应该是名词,不是动词。属性的命名规则与类的私有数据域相同,但不要加F前缀。
H,类的事件命名应以ON、BEFORE、AFTER作为前缀。

           2.5.4    文件命名规范
A,工程文件:工程文件应该赋予有意义的名字。可以取工程的全称组成单词的开始字符的大写,或者每个单词的缩写。也可以直接用单个单词命名。
B,窗体文件:窗体文件的名称必须能表达Form的用途,并且具有frm前缀。
C,数据模块文件:Data Module文件的名称必须能表达数据模块的用途,并且具有dm前缀。
D,远程数据模块文件:Remote Data Module文件的名称必须能表达远程数据模块的用途,并且具有rdm前缀。
E,单元文件:窗体及数据模块的单元文件名称应与相应的Form和数据模块名称相同。通用单元(如公共函数库、变量库、常量库等)的命名应尽量表达它的含义,并以Lib作为前缀。

          2.5.5     窗体、数据模块的命名规则
                      A,FORM类型的名称应当表达出FORM的用途,且要加Tfrm为前缀,后跟描述性的名称。
                       B,FORM实例的名称与相应的类型名称相同,但没有前缀T。
                       C,数据模块的类型名称应为:前缀Tdm + 描述性名称。
                     D,数据模块实例的名称与相应的类型名称相同,但没有前缀T。

          2.5.6    元件命名规则
                      A,组件命名标准和类的命名标准相似。不同的是他们有三个字符的标志性前缀,用来表明公司、个人等。作为前缀的三个字符要用小写。
                      B,元件的注册单元名称应为三个字符前缀加Reg,前缀与元件前缀相同。
C,元件实例命名规则:元件类型前缀 + 描述性名称。
D,DELPHI标准元件的类型前缀如下:
Standard Tab

mm TMainMenu
pm TPopupMenu
mmi TMainMenuItem
pmi TPopupMenuItem
lbl TLabel
edt TEdit
mem TMemo
btn TButton
cb TCheckBox
rb TRadioButton
lb TListBox
cb TComboBox
scb TScrollBar
gb TGroupBox
rg TRadioGroup
pnl TPanel
cl TCommandList

Additional Tab
 
bbtn TBitBtn
sb TSpeedButton
me TMaskEdit
sg TStringGrid
dg TDrawGrid
img TImage
shp TShape
bvl TBevel
sbx TScrollBox
clb TCheckListbox
spl TSplitter
stx TStaticText
cht TChart

Win32 Tab
  
tbc TTabControl
pgc TPageControl
il TImageList
re TRichEdit
tbr TTrackBar
prb TProgressBar
ud TUpDown
hk THotKey
ani TAnimate
dtp TDateTimePicker
tv TTreeView
lv TListView
hdr THeaderControl
stb TStatusBar
tlb TToolBar
clb TCoolBar

System Tab
  
tm TTimer
pb TPaintBox
mp TMediaPlayer
olec TOleContainer
ddcc TDDEClientConv
ddci TDDEClientItem
ddsc TDDEServerConv
ddsi TDDEServerItem

Internet Tab
  
csk TClientSocket
ssk TServerSocket
wbd TWebDispatcher
pp TPageProducer
tp TQueryTableProducer
dstp TDataSetTableProducer
nmdt TNMDayTime
nec TNMEcho
nf TNMFinger
nftp TNMFtp
nhttp TNMHttp
nMsg TNMMsg
nmsg TNMMSGServ
nntp TNMNNTP
npop TNMPop3
nuup TNMUUProcessor
smtp TNMSMTP
nst TNMStrm
nsts TNMStrmServ
ntm TNMTime
nudp TNMUdp
psk TPowerSock
ngs TNMGeneralServer
html THtml
url TNMUrl
sml TSimpleMail

Data Access Tab
  
ds TDataSource
tbl TTable
qry TQuery
sp TStoredProc
db TDataBase
ssn TSession
bm TBatchMove
usql TUpdateSQL

Data Controls Tab
  
dbg TDBGrid
dbn TDBNavigator
dbt TDBText
dbe TDBEdit
dbm TDBMemo
dbi TDBImage
dblb TDBListBox
dbcb TDBComboBox
dbch TDBCheckBox
dbrg TDBRadioGroup
dbll TDBLookupListBox
dblc TDBLookupComboBox
dbre TDBRichEdit
dbcg TDBCtrlGrid
dbch TDBChart

Decision Cube Tab
  
dcb TDecisionCube
dcq TDecisionQuery
dcs TDecisionSource
dcp TDecisionPivot
dcg TDecisionGrid
dcgr TDecisionGraph

QReport Tab
  
qr TQuickReport
qrsd TQRSubDetail
qrb TQRBand
qrcb TQRChildBand
qrg TQRGroup
qrl TQRLabel
qrt TQRText
qre TQRExpr
qrs TQRSysData
qrm TQRMemo
qrrt TQRRichText
qrdr TQRDBRichText
qrsh TQRShape
qri TQRImage
qrdi TQRDBMImage
qrcr TQRCompositeReport
qrp TQRPreview
qrch TQRChart

Win31 Tab
  
dbll TDBLookupList
dblc TDBLookupCombo
ts TTabSet
ol TOutline
tnb TTabbedNoteBook
nb TNoteBook
hdr THeader
flb TFileListBox
dlb TDirectoryListBox
dcb TDriveComboBox
fcb TFilterComboBox

Samples Tab
  
gg TGauge
cg TColorGrid
spb TSpinButton
spe TSpinEdit
dol TDirectoryOutline
cal TCalendar
ibea TIBEventAlerter

ActiveX Tab
  
cfx TChartFX
vsp TVSSpell
f1b TF1Book
vtc TVTChart
grp TGraph

Midas Tab
  
psp TDatasetProvider
cds TClientDataSet
dcom TDCOMConnection
olee TOleEnterpriseConnection
sck TSocketConnection
rms TRemoteServer
mid TmidasConnection

ADO Tab
  
adocn  ADOConnection 
adocm  ADOCommand
adods  ADODataSet
adot   ADOTable
adoq   ADOQuery
adosp  ADOStoreProc
rdsc   RDSConnection

InterBase Tab
   
ibt   IBTable
ibq   IBQuery
ibsp  IBStoreProc
ibd   IBDatabase
ibts  IBTransaction
ibu   IBUpdateSQL
ibds  IBDataSet
ibsql IBSQL
ibdi  IBDatabaseInfo
ibsm  IBSQLMonitor
ibe   IBEvents

III. 补充事项

3.1   本文档如有不明之处,或需要修改的,必须提请项目经理审批、核准。

3.2   文档如有变更,变更日期及变更人应在1.4中注明。


0 0

相关博文

我的热门文章

img
取 消
img