Fork me on GitHub

【ASP.NET MVC系列】浅谈表单和HTML辅助方法

ASP.NET MVC系列文章

【01】浅谈Google Chrome浏览器(理论篇)

【02】浅谈Google Chrome浏览器(操作篇)(上)

【03】浅谈Google Chrome浏览器(操作篇)(下)

【04】浅谈ASP.NET框架   

【05】浅谈ASP.NET MVC运行过程    

【06】浅谈ASP.NET MVC 控制器   

【07】浅谈ASP.NET MVC 路由   

【08】浅谈ASP.NET MVC 视图 

【09】浅谈ASP.NET MVC 视图与控制器传递数据

【10】浅谈jqGrid 在ASP.NET MVC中增删改查     

【11】浅谈ASP.NET 页面之间传值的几种方式

【12】浅谈缓存技术在ASP.NET中的运用       

【13】浅谈NuGet在VS中的运用      

【14】浅谈ASP.NET 程序发布过程           

【15】浅谈数据注解和验证           

【16】浅谈依赖注入

【17】浅谈表单和HTML辅助方法

【18】浅谈基于APS.NET身份验证

【19】浅谈ASP.NET MVC 模型

【20】浅谈ASP.NET MVC 单元测试

【21】浅谈ASP.NET MVC网络安全;

【22】浅谈ASP.NET MVC八大类扩展

【23】再谈ASP.NET MVC Routing

【24】浅谈ASP.NET 高级话题

【25】浅谈大型ASP.NET MVC项目(含DEMO)

【26】下一系列:ASP.NET WebAPI


 

一 概述

基于ASP.NET MVC基架开发模式中,我们很清楚View的扩展名:.cshtml,对该扩展名,不知是否有朋友研究过为啥将其如此命名?我且将它拆分成.cshtml=.cs(后台代码)+html(前端纯html标签代码)。

我们知道,MVC的本质目的是尽量做到前后端分离,View这样命名,是否有违背前后端分离这一原则呢?当然不是,相反,这样做却提高了代码的复用性,提高了编程的效率。

那有什么工具来解决该问题呢?HTML辅助方法。

本文将与大家分享HTML辅助方法,当然,HTML辅助方法是在表单上运用的,所以,我们会先大致提一些表单(Form)。HTML辅助方法,我们可大致归结为基于ASP.NET MVC基架的HTML辅助方法和自定义的

HTML扩展方法,前者不作为本章的重点(因为非常简单,使用时,只需调用相应的方法即可),后者才是本章的重点。

二 表单

关于表单的内容,将会从下图的四个方面的来论述:

(1)WebFormb表单与MVC表单的比较

(2)表单提交的方式和url:action和method特性

(3)表单请求方式

(4)数据输入的一般模式

(一)WebForm表单与MVC表单比较

1.WebForm表单主要是利用其强大的<form>标签,而MVC并未完全利用<form>标签;

2.WebForm主要利用服务器端控件,MVC主要利用基于MVC基架的HTML辅助方法,两者都用HTML标签

 3.WebForm页面与后台代码强绑定,而MVC与后台代码松耦合

(1)WebForm中,每个页面对应一个类,页面泪继承Page类,我们称为页面类,如上图中Default页面对应的类为_Default,

(2)每个页面由三部分组成:前端代码(Default.aspx),后台代码(Default.aspx.cs)和设计器(Default.aspx.designer.cs);

4.从性能上看,MVC比WebForm性能高。WebForm性能低的主要因素有如下几点:

(1)服务器端控件,消耗带宽,吃内存;

(2)ViewState垃圾数据; 

(二)表单提交的方式和url:action和method特性

action和method为<form>标签两个重要的特性

(1)action:指将<form>标签提交到何处,本质就是一个url;

(2)method:提交form的方法,主要为post和get,默认为get;

(三)表单请求方式

表单请求方式,主要为post和get,默认为get;

(四)数据输入的一般模式

 数据输入模式,一般分为两种模式:编辑-提交模式(Edit-and-Post)和选择-编辑-提交模式(Selct-Edit-Post)

三 HTML辅助方法

基于ASP.NET MVC基架的HTML辅助方法,大致分为内置HTM辅助方法(也叫基于MVC基架的HTML辅助方法)和自定义HTML辅助方法。 

(一)基于MVC基架的HTML辅助方法

通过反汇编工具查看System.Web.Mvc.Html下的辅助方法,如下图所以。

由于基于MVC基架的辅方法比较简单,使用时只需调用即可,故本节不会花较大篇幅讲解,只是大致提及一下。

1.我们随便查看InputExtensions和LableExtensions辅助方法

InputExtensions

public static class InputExtensions
{
    // Methods
    public static MvcHtmlString CheckBox(this HtmlHelper htmlHelper, string name);
    public static MvcHtmlString CheckBox(this HtmlHelper htmlHelper, string name, bool isChecked);
    public static MvcHtmlString CheckBox(this HtmlHelper htmlHelper, string name, IDictionary<string, object> htmlAttributes);
    public static MvcHtmlString CheckBox(this HtmlHelper htmlHelper, string name, object htmlAttributes);
    public static MvcHtmlString CheckBox(this HtmlHelper htmlHelper, string name, bool isChecked, IDictionary<string, object> htmlAttributes);
    public static MvcHtmlString CheckBox(this HtmlHelper htmlHelper, string name, bool isChecked, object htmlAttributes);
    public static MvcHtmlString CheckBoxFor<TModel>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, bool>> expression);
    public static MvcHtmlString CheckBoxFor<TModel>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, bool>> expression, IDictionary<string, object> htmlAttributes);
    public static MvcHtmlString CheckBoxFor<TModel>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, bool>> expression, object htmlAttributes);
    private static MvcHtmlString CheckBoxHelper(HtmlHelper htmlHelper, ModelMetadata metadata, string name, bool? isChecked, IDictionary<string, object> htmlAttributes);
    public static MvcHtmlString Hidden(this HtmlHelper htmlHelper, string name);
    public static MvcHtmlString Hidden(this HtmlHelper htmlHelper, string name, object value);
    public static MvcHtmlString Hidden(this HtmlHelper htmlHelper, string name, object value, IDictionary<string, object> htmlAttributes);
    public static MvcHtmlString Hidden(this HtmlHelper htmlHelper, string name, object value, object htmlAttributes);
    public static MvcHtmlString HiddenFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression);
    public static MvcHtmlString HiddenFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, IDictionary<string, object> htmlAttributes);
    public static MvcHtmlString HiddenFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, object htmlAttributes);
    private static MvcHtmlString HiddenHelper(HtmlHelper htmlHelper, ModelMetadata metadata, object value, bool useViewData, string expression, IDictionary<string, object> htmlAttributes);
    private static MvcHtmlString InputHelper(HtmlHelper htmlHelper, InputType inputType, ModelMetadata metadata, string name, object value, bool useViewData, bool isChecked, bool setId, bool isExplicitValue, string format, IDictionary<string, object> htmlAttributes);
    public static MvcHtmlString Password(this HtmlHelper htmlHelper, string name);
    public static MvcHtmlString Password(this HtmlHelper htmlHelper, string name, object value);
    public static MvcHtmlString Password(this HtmlHelper htmlHelper, string name, object value, IDictionary<string, object> htmlAttributes);
    public static MvcHtmlString Password(this HtmlHelper htmlHelper, string name, object value, object htmlAttributes);
    public static MvcHtmlString PasswordFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression);
    public static MvcHtmlString PasswordFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, IDictionary<string, object> htmlAttributes);
    public static MvcHtmlString PasswordFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, object htmlAttributes);
    private static MvcHtmlString PasswordHelper(HtmlHelper htmlHelper, ModelMetadata metadata, string name, object value, IDictionary<string, object> htmlAttributes);
    public static MvcHtmlString RadioButton(this HtmlHelper htmlHelper, string name, object value);
    public static MvcHtmlString RadioButton(this HtmlHelper htmlHelper, string name, object value, bool isChecked);
    public static MvcHtmlString RadioButton(this HtmlHelper htmlHelper, string name, object value, IDictionary<string, object> htmlAttributes);
    public static MvcHtmlString RadioButton(this HtmlHelper htmlHelper, string name, object value, object htmlAttributes);
    public static MvcHtmlString RadioButton(this HtmlHelper htmlHelper, string name, object value, bool isChecked, IDictionary<string, object> htmlAttributes);
    public static MvcHtmlString RadioButton(this HtmlHelper htmlHelper, string name, object value, bool isChecked, object htmlAttributes);
    public static MvcHtmlString RadioButtonFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, object value);
    public static MvcHtmlString RadioButtonFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, object value, IDictionary<string, object> htmlAttributes);
    public static MvcHtmlString RadioButtonFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, object value, object htmlAttributes);
    private static MvcHtmlString RadioButtonHelper(HtmlHelper htmlHelper, ModelMetadata metadata, object model, string name, object value, bool? isChecked, IDictionary<string, object> htmlAttributes);
    public static MvcHtmlString TextBox(this HtmlHelper htmlHelper, string name);
    public static MvcHtmlString TextBox(this HtmlHelper htmlHelper, string name, object value);
    public static MvcHtmlString TextBox(this HtmlHelper htmlHelper, string name, object value, IDictionary<string, object> htmlAttributes);
    public static MvcHtmlString TextBox(this HtmlHelper htmlHelper, string name, object value, object htmlAttributes);
    public static MvcHtmlString TextBox(this HtmlHelper htmlHelper, string name, object value, string format);
    public static MvcHtmlString TextBox(this HtmlHelper htmlHelper, string name, object value, string format, IDictionary<string, object> htmlAttributes);
    public static MvcHtmlString TextBox(this HtmlHelper htmlHelper, string name, object value, string format, object htmlAttributes);
    public static MvcHtmlString TextBoxFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression);
    public static MvcHtmlString TextBoxFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, IDictionary<string, object> htmlAttributes);
    public static MvcHtmlString TextBoxFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, object htmlAttributes);
    public static MvcHtmlString TextBoxFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, string format);
    public static MvcHtmlString TextBoxFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, string format, IDictionary<string, object> htmlAttributes);
    public static MvcHtmlString TextBoxFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, string format, object htmlAttributes);
    private static MvcHtmlString TextBoxHelper(this HtmlHelper htmlHelper, ModelMetadata metadata, object model, string expression, string format, IDictionary<string, object> htmlAttributes);
    private static RouteValueDictionary ToRouteValueDictionary(IDictionary<string, object> dictionary);
}

LableExtensions

public static class LabelExtensions
{
    // Methods
    public static MvcHtmlString Label(this HtmlHelper html, string expression);
    public static MvcHtmlString Label(this HtmlHelper html, string expression, IDictionary<string, object> htmlAttributes);
    public static MvcHtmlString Label(this HtmlHelper html, string expression, object htmlAttributes);
    public static MvcHtmlString Label(this HtmlHelper html, string expression, string labelText);
    public static MvcHtmlString Label(this HtmlHelper html, string expression, string labelText, IDictionary<string, object> htmlAttributes);
    public static MvcHtmlString Label(this HtmlHelper html, string expression, string labelText, object htmlAttributes);
    internal static MvcHtmlString Label(this HtmlHelper html, string expression, string labelText, IDictionary<string, object> htmlAttributes, ModelMetadataProvider metadataProvider);
    internal static MvcHtmlString Label(this HtmlHelper html, string expression, string labelText, object htmlAttributes, ModelMetadataProvider metadataProvider);
    public static MvcHtmlString LabelFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression);
    public static MvcHtmlString LabelFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression, IDictionary<string, object> htmlAttributes);
    public static MvcHtmlString LabelFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression, object htmlAttributes);
    public static MvcHtmlString LabelFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression, string labelText);
    public static MvcHtmlString LabelFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression, string labelText, IDictionary<string, object> htmlAttributes);
    public static MvcHtmlString LabelFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression, string labelText, object htmlAttributes);
    internal static MvcHtmlString LabelFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression, string labelText, IDictionary<string, object> htmlAttributes, ModelMetadataProvider metadataProvider);
    internal static MvcHtmlString LabelFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression, string labelText, object htmlAttributes, ModelMetadataProvider metadataProvider);
    public static MvcHtmlString LabelForModel(this HtmlHelper html);
    public static MvcHtmlString LabelForModel(this HtmlHelper html, IDictionary<string, object> htmlAttributes);
    public static MvcHtmlString LabelForModel(this HtmlHelper html, object htmlAttributes);
    public static MvcHtmlString LabelForModel(this HtmlHelper html, string labelText);
    public static MvcHtmlString LabelForModel(this HtmlHelper html, string labelText, IDictionary<string, object> htmlAttributes);
    public static MvcHtmlString LabelForModel(this HtmlHelper html, string labelText, object htmlAttributes);
    internal static MvcHtmlString LabelHelper(HtmlHelper html, ModelMetadata metadata, string htmlFieldName, string labelText = null, IDictionary<string, object> htmlAttributes = null);
}

2.在ASP.NET MVC5 高级编程(Jon Galloway,Brad Wilson,K.Scott Allen,David Matson 著 ,孙远帅 译) 中,作者将HTML辅助方法大致分为下图几类

(二)自定义的HTML辅助方法

关于自定义HTML辅助方法,主要从下图五个角度讲解。

1.为什么要扩展辅助方法

(1)何为扩展?

从汉语字面意义理解,即在现有的基础上进行修改(修改现有辅助方法)、增加(自定义MVC基架没有的辅助方法)等操作。

(2)扩展的作用?

首先,从MVC基架现有的某些HTML辅助方法,其某些属性,如样式等无法满足现有需求,需要扩展;

其次,现有需求的某些辅助方法,如Image辅助辅助方法,File辅助方法等,MVC基架并未提供,需要扩展;

最后,扩展的最终目的是提高代码的复用,提高编码效率;

2.用反汇编工具查看MVC源码是如何扩展的

(1)我们查看MVC是如何定义强类型和弱类型的,以Html.Lable为例,我们容易得出三个结论:

1)程序集为System.Web.Mvc

2)命名空间为System.Web.Mvc.Html

3)弱类型方法名字直接为纯html对应名字

4)强类型方法名字=若类型名字+For 

5)辅助方法的返回类型均为MvcHtmlString

(2)我们用反汇编工具查看一下

(3)总结

根据如上(1)(2)分析,我们知道定义一个HTML辅助方法的步骤

1)命名空间为System.Web.Mvc

2)弱类型方法名字直接为纯html对应名字

3)强类型方法名字=若类型名字+For 

4)辅助方法的返回类型均为MvcHtmlString

3.扩展弱类型辅助方法

 1 //Image弱类型
 2 public static MvcHtmlString Image(this HtmlHelper helper, string id, string url, string width, string height, string alternateText, object htmlAttributes)
 3         {
 4             //创建img标签
 5             TagBuilder imgTagBulider = new TagBuilder("img");
 6 
 7             //为img标签添加属性:id,url,alternateText,htmlAttributes
 8             imgTagBulider.GenerateId(id);
 9             imgTagBulider.MergeAttribute("src", url);
10             imgTagBulider.MergeAttribute("width", width);
11             imgTagBulider.MergeAttribute("height", height);
12             imgTagBulider.MergeAttribute("src", url);
13             imgTagBulider.MergeAttribute("alt", alternateText);
14             imgTagBulider.MergeAttributes(new RouteValueDictionary(htmlAttributes));
15 
16             // 输出img标签
17             return MvcHtmlString.Create(imgTagBulider.ToString());
18         }

4.扩展强类型辅助方法

 1  //Image强类型
 2 public static MvcHtmlString ImageFor<TModel, TValue>(this HtmlHelper<TModel> html,Expression<Func<TModel,TValue>> expression,string url, string width, string height, string alternateText, Dictionary<TModel, TValue> htmlAttributes)
 3         {
 4             string modelName = ExpressionHelper.GetExpressionText(expression);//从Lambda表达式中获取模型对应属性的名称
 5             //创建img标签
 6             TagBuilder imgTagBulider = new TagBuilder("img");
 7 
 8             //为img标签添加属性:id,url,alternateText,htmlAttributes
 9             imgTagBulider.GenerateId(modelName);
10             imgTagBulider.MergeAttribute("src", url);
11             imgTagBulider.MergeAttribute("width", width);
12             imgTagBulider.MergeAttribute("height", height);
13             imgTagBulider.MergeAttribute("src", url);
14             imgTagBulider.MergeAttribute("alt", alternateText);
15             imgTagBulider.MergeAttributes(new RouteValueDictionary(htmlAttributes));
16 
17             return MvcHtmlString.Create(imgTagBulider.ToString(TagRenderMode.SelfClosing));
18         }

 5.完整代码

Index.cshtml

1 @model  HTMLHelperDemo.Models.UserInfo
2 
3 
4 <div>---------------Image弱类型扩展------------------</div>
5 <div>@Html.Image("ImageID", "/Images/hgspb.jpg", "300","300","自定义图片",null)</div>
6 <div>---------------Image强类型扩展------------------</div>
7 <div>@Html.ImageFor(m=>m.UserName, "/Images/hgspb.jpg", "300", "300", "自定义图片", null)</div>
8  
View Code

DefaultController

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Web;
 5 using System.Web.Mvc;
 6 
 7 namespace HTMLHelperDemo.Controllers
 8 {
 9     public class DefaultController : Controller
10     {
11         // GET: Default
12         public ActionResult Index()
13         {
14             return View();
15         }
16     }
17 }
View Code

MyHtmlHelperExtension.cs

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Web;
 5 
 6 using System.Web.Routing;
 7 using System.Linq.Expressions;
 8 namespace System.Web.Mvc
 9 {
10     public static class ImageExtensions
11     {
12         //Image弱类型
13         public static MvcHtmlString Image(this HtmlHelper helper, string id, string url, string width, string height, string alternateText, object htmlAttributes)
14         {
15             //创建img标签
16             TagBuilder imgTagBulider = new TagBuilder("img");
17 
18             //为img标签添加属性:id,url,alternateText,htmlAttributes
19             imgTagBulider.GenerateId(id);
20             imgTagBulider.MergeAttribute("src", url);
21             imgTagBulider.MergeAttribute("width", width);
22             imgTagBulider.MergeAttribute("height", height);
23             imgTagBulider.MergeAttribute("src", url);
24             imgTagBulider.MergeAttribute("alt", alternateText);
25             imgTagBulider.MergeAttributes(new RouteValueDictionary(htmlAttributes));
26 
27             // 输出img标签
28             return MvcHtmlString.Create(imgTagBulider.ToString());
29         }
30         //Image强类型
31         public static MvcHtmlString ImageFor<TModel, TValue>(this HtmlHelper<TModel> html,Expression<Func<TModel,TValue>> expression,string url, string width, string height, string alternateText, Dictionary<TModel, TValue> htmlAttributes)
32         {
33             string modelName = ExpressionHelper.GetExpressionText(expression);//从Lambda表达式中获取模型对应属性的名称
34             //创建img标签
35             TagBuilder imgTagBulider = new TagBuilder("img");
36 
37             //为img标签添加属性:id,url,alternateText,htmlAttributes
38             imgTagBulider.GenerateId(modelName);
39             imgTagBulider.MergeAttribute("src", url);
40             imgTagBulider.MergeAttribute("width", width);
41             imgTagBulider.MergeAttribute("height", height);
42             imgTagBulider.MergeAttribute("src", url);
43             imgTagBulider.MergeAttribute("alt", alternateText);
44             imgTagBulider.MergeAttributes(new RouteValueDictionary(htmlAttributes));
45 
46             return MvcHtmlString.Create(imgTagBulider.ToString(TagRenderMode.SelfClosing));
47         }
48     }
49 }
50     
51  
View Code

图解

 四  HTML辅助方法的工作原理

关于HTML辅助方法工做原理,这里不做深入研讨,只是描述一下工作原理的轮廓。

1.MVC中,View的后缀为.cshtml,我们可以将其拆分为:.cshtml=.cs+html,即由后台.cs代码+html标签构成;

2.既然View是由后台代码.cs+html标签构成,那么什么标签能满足这两个条件呢?HTML辅助方法。由此,我们知道HTML辅助方法扮演后台代码和前端HTML代码的中间者,桥梁;

3.既然HTML代码扮演后台代码和前端HTML桥梁,那么其与后台有哪些联系呢?

 (1)与Model的联系,如HTML强辅助方法,使用Lambda表达式

 (2)与Conteller联系,如Html.ActonLink

(3)与Route联系,如Html.RouteLink;

(4)与ModelState联系,如在验证输入值的合法性时,若验证错误,错误消息存在模型状态中,然后返回给Html相应的辅助方法

.......

4.我们知道了HTML辅助方法与后台的联系,那么与后台联系之后,接下来做什么呢?渲染成HTML,返回给浏览器

如上,我们大致分析了HTML辅助方法的工作原理步骤,下面我们将要的画图分析一下

四  参考文献

【01】C#高级编程(第七版)  (Christian Nagel,Bill Evjen和Jay Glynn 编著,李铭 译,黄静 审校) 

五 作者关于评论的建议

欢迎读者朋友们广提意见,您宝贵的意见,是我写作的动力。相互学习,共同进步!
(一) 关于文章内容
1.简单,回复1(请指出简单因素)
2.一般,回复2
3.适度,回复3
4.较难,回复4
5.很难,回复5(请指出很难因素)
(二) 文章讲解
1.一般,回复6
2.良好,回复7
3.易懂,回复8
4.复杂,回复9(请指出复杂因素)
(三) 关于其他意见
10.若有其他意见,在评论区评价即可;
11.关于评价内容,不好的评价内容(除人身攻击外),具有建设性建议的评价内容,一定保存
12.其他评价,有可能会被删除

六   版权区

  • 感谢您的阅读,若有不足之处,欢迎指教,共同学习、共同进步。
  • 博主网址:http://www.cnblogs.com/wangjiming/。
  • 极少部分文章利用读书、参考、引用、抄袭、复制和粘贴等多种方式整合而成的,大部分为原创。
  • 如您喜欢,麻烦推荐一下;如您有新想法,欢迎提出,邮箱:2098469527@qq.com。
  • 可以转载该博客,但必须著名博客来源。
posted @ 2018-01-21 07:26  Alan_beijing  阅读(4265)  评论(4编辑  收藏  举报