Asp.net MVC 3实例学习之ExtShop(五)——产品详细页
在产品详细页需要使用到tab控件,在jquery的ui包已包含改控件,因而将相应文件链接加到母版页就可以了。
打开“ProductController”文件,在里面添加一个Details操作,代码如下:
1 | public ActionResult Details ( int id ) |
2 | { |
3 | var q = dc . T_Products . Single ( m = > m . ProductID = = id ) ; |
4 | return View ( q ) ; |
5 | } |
6 |
完成后创建对应的视图页,并完成整个页面框架,代码如下:
1 | @ model Extshop . Models . T_Products |
2 | |
3 | @ { |
4 | ViewBag . Title = Model . Title ; |
5 | PageData [ " id " ] = Model . CategoryID ; |
6 | } |
7 | |
8 | < div class = " nav " > |
9 | < a href = " @Url.Action( " " , " Catalog " ) " > 产品 < / a > |
10 | @ { Html . RenderAction ( " Navbar " , " Catalog " , new { id = PageData [ " id " ] } ) ; } |
11 | @ Html . Raw ( " >> " ) |
12 | @ Model . Title |
13 | < / div > < br / > |
14 | < div id = " contentMain " style = " width:760px; " > |
15 | < span class = " header " style = " width:750px; " id = " producttitle " > @ Model . Title < / span > |
16 | < div class = " img " > |
17 | < a href = " /images/products/@Model.LargeImageUrl " rel = " lightbox " > < img src = " /images/products/@Model.SamllImageUrl " alt = " @Model.Title " width = " 170 " height = " 190 " / > < / a > |
18 | < / div > |
19 | < div class = " details " > |
20 | < ul > |
21 | < li > 市场价格: < del > @ Model . MarketPrice . ToString ( " C " ) < / del > < / li > |
22 | < li id = ' unitprice ' > 当前价格: @ Model . UnitPrice . ToString ( " C " ) < / li > |
23 | < li > < span > 用户评价: < / span > |
24 | < div class = ' rating ' id = ' rating1 ' > |
25 | < input name = " @Model.ProductID.ToString( " Star0 " ) " type = " radio " class = " star " disabled = " disabled " value = " 1 " @ ( Model . TotalRating = = 1 ? " checked='checked' " : " " ) / > |
26 | < input name = " @Model.ProductID.ToString( " Star0 " ) " type = " radio " class = " star " disabled = " disabled " value = " 2 " @ ( Model . TotalRating = = 2 ? " checked='checked' " : " " ) / > |
27 | < input name = " @Model.ProductID.ToString( " Star0 " ) " type = " radio " class = " star " disabled = " disabled " value = " 3 " @ ( Model . TotalRating = = 3 ? " checked='checked' " : " " ) / > |
28 | < input name = " @Model.ProductID.ToString( " Star0 " ) " type = " radio " class = " star " disabled = " disabled " value = " 4 " @ ( Model . TotalRating = = 4 ? " checked='checked' " : " " ) / > |
29 | < input name = " @Model.ProductID.ToString( " Star0 " ) " type = " radio " class = " star " disabled = " disabled " value = " 5 " @ ( Model . TotalRating = = 5 ? " checked='checked' " : " " ) / > |
30 | < / div > |
31 | < / li > |
32 | < li > 制造厂商: @ Model . Manufacturers < / li > |
33 | < li > 产品型号: @ Model . Model < / li > |
34 | < li > 库存情况: @ ( Model . Stock > 0 ? @ Html . Raw ( " color='blue'>有货 " ) : @ Html . Raw ( " color='red'>缺货 " ) ) < / li > |
35 | < li > < hr / > < / li > |
36 | < li > < a href = ' # ' class = ' cart ' onclick = ' ' > < img alt = " " width = " 50 " height = " 22 " src = ' / images / buy . jpg ' / > < / a > < / li > |
37 | < / ul > |
38 | < / div > |
39 | < div class = ' clear ' > < / div > |
40 | < div id = " tabs " style = " width:740px;margin:auto; " > |
41 | < ul style = " width:727px; " > |
42 | < li > < a href = " #tabs-1 " > 产品描述 < / a > < / li > |
43 | < li > < a href = " #tabs-2 " > 规格参数 < / a > < / li > |
44 | < li > < a href = " #tabs-3 " > 保修条款 < / a > < / li > |
45 | < li > < a href = " #tabs-4 " > 评论 < / a > < / li > |
46 | < / ul > |
47 | < div id = " tabs-1 " > |
48 | < p > @ Model . Description < / p > |
49 | < / div > |
50 | < div id = " tabs-2 " style = " border:0; " > |
51 | < br / > < br / > |
52 | < table id = " " width = " 100% " cellpadding = " 0 " cellspacing = " 0 " border = " 0 " > @ Html . Raw ( @ Model . Specification ) < / table > |
53 | < / div > |
54 | < div id = " tabs-3 " > |
55 | < p > @ Model . Warranty < / p > |
56 | < / div > |
57 | < div id = " tabs-4 " > |
58 | < / div > |
59 | < / div > |
60 | < br / > |
61 | < / div > |
62 | |
63 |
1 | $ ( " #tabs " ) . tabs ( ) ; |
现在要完成导航信息,打开CatalogController文件,添加一个Navbar操作,代码如下:
1 | [ ChildActionOnly ] |
2 | public ActionResult Navbar ( string id ) |
3 | { |
4 | List < string > idlist = new List < string > ( ) ; |
5 | idlist . Add ( id ) ; |
6 | for ( int i = 0 ; i < ( id . Length - 2 ) ; i = i + 3 ) |
7 | { |
8 | idlist . Add ( id . Substring ( 0 , i + 3 ) ) ; |
9 | } |
10 | var q = dc . T_Categories . Where ( m = > idlist . Contains ( m . CategoryID ) ) . OrderBy ( m = > m . CategoryID ) ; |
11 | return PartialView ( q ) ; |
12 | } |
13 |
代码首先获取产品类别的父类编号,然后查询出父类进行显示。右键单击“Navbar”创建分页页面,页面的内容如下:
1 | @ model IEnumerable < Extshop . Models . T_Categories > |
2 | |
3 | @ { |
4 | foreach ( var c in Model ) |
5 | { |
6 | @ Html . Raw ( " >> " ) < a href = ' @ Url . Action ( " List " , " Catalog " , new { id = c . CategoryID } ) ' > @ c . Titel < / a > |
7 | } |
8 | } |
现在要完成评论的显示。评论我们使用分别页面很容易显示。首先在产品详细信息页内的id为“tabs-4”的div下添加以下代码:
1 | < div style = " clear:both; " > < / div > |
2 | < div id = " CommentList " > |
3 | @ { Html . RenderAction ( " Index " , " Comment " , new { id = @ Model . ProductID , page = 1 } ) ; } |
4 | < / div > |
5 | < br / > |
6 | @ { Html . RenderAction ( " AddComment " , " Comment " , new { id = @ Model . ProductID } ) ; } |
7 |
代码第3行使用一个分部页面显示评论内容。第6行就用分部页面显示评论添加表单。
新建一个名称为“CommentController”的控制器,并修改index操作代码如下:
1 | public ActionResult Index ( int id , int ? page ) |
2 | { |
3 | ViewData [ " ProdcutID " ] = id ; |
4 | PagedList < T_Comment > q = dc . T_Comment . Where ( m = > m . ProductID = = id ) . OrderByDescending ( m = > m . CreateTime ) . ToPagedList ( page ? ? 1 , 2 ) ; |
5 | return PartialView ( q ) ; |
6 | } |
7 |
代码中第1个参数id表示的是产品的id,第2个参数是评论的当前页。和产品列表一样,使用mvcPager进行分页。完成后创建分部视图页,并将其代码修改如下:
1 | @ using Webdiyer . WebControls . Mvc ; |
2 | @ model PagedList < Extshop . Models . T_Comment > |
3 | @ { |
4 | foreach ( var c in Model ) |
5 | { |
6 | < div class = " row " > |
7 | < div class = " title " > @ c . Title < / div > |
8 | < b > @ c . Username < / b > & nbsp ; 发表于 @ ( c . CreateTime ) < br / > |
9 | @ c . Description |
10 | < / div > |
11 | } |
12 | } |
13 | < br / > |
14 | < div class = ' pagenav ' > @ Ajax . Pager ( Model , new PagerOptions { PageIndexParameterName = " page " } , new AjaxOptions { UpdateTargetId = " CommentList " } ) < / div > |
15 |
代码中第14行使用了Ajax分页的方法,这样,就可以使用Ajax自动更新该部分。在AjaxOption中定义了UpdateTargetId参数为“CommentList”,意味着当Ajax加载数据后,会自动更新id为“CommentList”的html元素内的内容。
最后要完成的是评论添加分部视图。首先需要创建一个表单模型。在Models目录创建一个名称为“CommentModels”的类,并将类代码修改如下:
1 | public class CommentModels |
2 | { |
3 | |
4 | [ Display ( Name = " 评分 " ) ] |
5 | public int Rating { get ; set ; } |
6 | |
7 | [ Required ( ErrorMessage = " 请输入“标题” " ) ] |
8 | [ Display ( Name = " 标题 " ) ] |
9 | public string Title { get ; set ; } |
10 | |
11 | [ Required ( ErrorMessage = " 请输入“内容” " ) ] |
12 | [ Display ( Name = " 内容 " ) ] |
13 | public string Description { get ; set ; } |
14 | |
15 | } |
16 |
我们只定义需要用户输入的3个项就行了,产品编号和发表用户这些可以从其它地方获取。完成后,切换到CommentController控制器文件,添加显示表单的控制器,其代码如下:
1 | [ ChildActionOnly ] |
2 | public ActionResult AddComment ( int id ) |
3 | { |
4 | ViewData [ " ProductID " ] = id ; |
5 | return PartialView ( ) ; |
6 | } |
7 |
在这里需要注意的是要将产品编号带到分部视图。然后创建分部视图,代码如下:
1 | @ model Extshop . Models . CommentModels |
2 | |
3 | @ using ( Ajax . BeginForm ( " AddComment " , " Comment " , new { id = ViewData [ " ProductID " ] } , new AjaxOptions { OnSuccess = " CommentSuccess " , LoadingElementId = " CommentLoad " , UpdateTargetId = " CommentMsg " |
4 | , OnBegin = " CommentBegin " } , new { id = " CommentForm " } ) ) |
5 | { |
6 | < div > |
7 | < fieldset > |
8 | < legend > < / legend > |
9 | < div style = " width:600px;display:block;height:30px; " > |
10 | < div id = ' rating - select ' style = " width:300px; " > |
11 | @ Html . LabelFor ( m = > m . Rating ) |
12 | @ Html . DropDownListFor ( m = > m . Rating , new SelectList ( new Dictionary < string , string > { |
13 | { " 1 " , " 1 " } , |
14 | { " 2 " , " 2 " } , |
15 | { " 3 " , " 3 " } , |
16 | { " 4 " , " 4 " } , |
17 | { " 5 " , " 5 " } |
18 | } , " Key " , " Value " ) , new { @ class = " star " , width = " 120 " } ) |
19 | < / div > |
20 | < div class = " error " > |
21 | @ Html . ValidationMessageFor ( m = > m . Rating ) |
22 | < / div > |
23 | < / div > |
24 | < p style = " width:600px;display:block; " > |
25 | @ Html . LabelFor ( m = > m . Title ) |
26 | @ Html . TextBoxFor ( m = > m . Title , new { style = " width:500px; " } ) |
27 | < / p > |
28 | < div class = " error " > |
29 | @ Html . ValidationMessageFor ( m = > m . Title ) |
30 | < / div > |
31 | < p style = " width:600px;display:block; " > |
32 | @ Html . LabelFor ( m = > m . Description ) |
33 | @ Html . TextAreaFor ( m = > m . Description , new { style = " width:500px; " } ) |
34 | < / p > |
35 | < div class = " error " > |
36 | @ Html . ValidationMessageFor ( m = > m . Description ) |
37 | < / div > |
38 | < p style = " text-align:center;width:600px; " > |
39 | < input id = " ComentSubmit " type = " submit " value = " 保存 " / > |
40 | < / p > |
41 | < p style = " text-align:center;display:none; " id = " CommentLoad " > < img src = " /Images/blue-loading.gif " alt = " 正在保存…… " / > < / p > |
42 | < p style = " text-align:center;color:Red; " id = " CommentMsg " > < / p > |
43 | < / fieldset > |
44 | < / div > |
45 | } |
46 | |
47 | < script type = " text/javascript " > |
48 | function CommentSuccess ( e ) { |
49 | $ ( " #CommentForm input " ) . removeAttr ( " readonly " ) ; |
50 | $ ( " #CommentSubmit " ) . removeAttr ( " disabled " ) ; |
51 | if ( e . Success ) { |
52 | $ ( " #CommentForm " ) [ 0 ] . reset ( ) ; |
53 | $ ( " #CommentMsg " ) . html ( e . Message ) ; |
54 | var url = window . location ; |
55 | re = / ( [ 0 - 9 ] * ) [ # ] ? $ / ig ; |
56 | var r = re . exec ( url ) |
57 | if ( r ) { |
58 | for ( var i = 0 ; i < r . length - 1 ; i + + ) { |
59 | if ( ! isNaN ( r [ i ] ) ) { |
60 | $ ( " #CommentList " ) . load ( " /Comment/ " + r [ i ] + " /1 " ) ; |
61 | } |
62 | } |
63 | } |
64 | } else { |
65 | $ ( " #CommentMsg " ) . html ( e . Message ) ; |
66 | } |
67 | } |
68 | |
69 | function CommentBegin ( e ) { |
70 | $ ( " #CommentForm input " ) . attr ( " readonly " , true ) ; |
71 | $ ( " #CommentSubmit " ) . attr ( " disabled " , " disabled " ) ; |
72 | $ ( " #CommentMsg " ) . html ( " " ) ; |
73 | } |
74 | |
75 | < / script > |
表单将已Ajax的方式提交,所以在这里使用的是Ajax.BeginForm,而不是Html.BeginForm。表单提交的路径将会是“/Comment/AddComment/{id}”,其中的id是产品的编号。代码第12行到18行将创建一个select元素用来生成评价输入控件。代码54行到62行的作用是评论保存后,通过页面路径获取产品编号,然后通过Ajax更新一下评论分部视图的显示。
余下的工作是完成评论保存控制器,其代码如下:
1 | [ HttpPost ] |
2 | public JsonResult AddComment ( int id , CommentModels model ) |
3 | { |
4 | if ( ModelState . IsValid ) |
5 | { |
6 | if ( User . Identity . IsAuthenticated ) |
7 | { |
8 | try |
9 | { |
10 | T_Comment comment = new T_Comment ( ) ; |
11 | comment . Description = model . Description ; |
12 | comment . ProductID = id ; |
13 | comment . Rating = model . Rating ; |
14 | comment . Title = model . Title ; |
15 | comment . Username = User . Identity . Name ; |
16 | comment . CreateTime = DateTime . Now ; |
17 | try |
18 | { |
19 | dc . T_Comment . InsertOnSubmit ( comment ) ; |
20 | dc . SubmitChanges ( ) ; |
21 | return Json ( new { Success = true , Message = " 评论已保存! " } , JsonRequestBehavior . AllowGet ) ; |
22 | } |
23 | catch ( Exception e ) |
24 | { |
25 | return Json ( new { Success = false , Message = " 评论保存失败:数据错误! " } , JsonRequestBehavior . AllowGet ) ; |
26 | } |
27 | } |
28 | catch |
29 | { |
30 | return Json ( new { Success = false , Message = " 评论保存失败:数据错误! " } , JsonRequestBehavior . AllowGet ) ; |
31 | } |
32 | } |
33 | else |
34 | { |
35 | return Json ( new { Success = false , Message = " 评论保存失败:请先登录! " } , JsonRequestBehavior . AllowGet ) ; |
36 | } |
37 | } |
38 | return Json ( new { Success = false , Message = " 添加评论失败:提交的数据存在错误! " } , JsonRequestBehavior . AllowGet ) ; |
39 | } |
40 |
代码第1行表示改操作是接收Post的操作。AddComment操作将接收两个参数,第1个是产品编号,第2个是用户提交的评论内容。因为使用Ajax提交,所以该操作不需要返回视图,只需要返回Json数据,所以操作的返回值是JsonResult。第4句判断用户的输入是否符合要求。第6句判断用户是否已经登录,如果没有登录,发送错误信息提示用户登录。
这样,整个产品详细信息页就完成了。不过,要使程序正常运行,还需要修改路由表,打开Global.asax.cs文件,在路由中加入以下路由:
1 | routes . MapRoute ( |
2 | " Comment1 " , // Route name |
3 | " Comment/AddComment/{id} " , // URL with parameters |
4 | new { controller = " Comment " , action = " AddComment " , id = 1 } // Parameter defaults |
5 | ) ; |
6 | |
7 | routes . MapRoute ( |
8 | " Comment " , // Route name |
9 | " Comment/{id}/{page} " , // URL with parameters |
10 | new { controller = " Comment " , action = " Index " , page = 1 } // Parameter defaults |
11 | ) ; |
12 |
第1个路由指示添加评论是如何路由的,如果没有这个,则全部会按第2个路由执行,全部操作转到Index操作去了,这样就会发生错误。