在產品展示中,通常涉及產品的展示方式、查詢、排序、分頁,本篇就在ASP.NET MVC下,使用Boostrap來實現。
源碼放在了GitHub: https://github.com/darrenji/ProductsSearchSortPage
先上效果圖:
最上面是搜索和排序,每次點擊搜索條件、排序,或者刪除搜索條件都會觸發異步加載。
中間部分為產品展示,提供了列表和格子這2種顯示方式。
最下方為分頁。
能實現的功能包括:
○ 點擊某一個搜索條件,該搜索條件被選中,選中項以標簽的形式顯示到"搜索條件"欄中,觸發異步加載
○ 點擊排序條件,該排序條件被選中,觸發異步加載
○ 刪除"搜索條件"欄中的搜索條件,觸發異步加載
實現的思路大致是:
○ 搜索、排序區域是Bootstrap的表格
○ 產品展示、及切換2中展示方式都借助Boostrap來實現
○ 分頁導航部分同樣借助Bootstrap來實現
○ 搜索條件的顯示是通過把異步加載到的數據填充到tmpl模版,然后追加到頁面對應區域
○ 產品展示同樣通過tmpl模版實現
○ 分頁導航用到了jquery的一個分頁插件,后面介紹
○ 每一個搜索條件、排序條件都有對應的隱藏域,當觸發頁面事件,就把值放在隱藏域中后,再傳遞給controller
產品模型 Models/Product.cs
public class Product{public int Id { get; set; }public string Name { get; set; }public string Description { get; set; }public string Category { get; set; }public string Brand { get; set; }public decimal Price { get; set; }public string ImageUrl { get; set; }public int Age { get; set; }}
關於搜索排序分頁的基類 Models/QueryBase.cs
public class QueryBase{public int PageIndex { get; set; }public int PageSize { get; set; }public short PaiXu { get; set; }}
產品的搜索排序分頁派生於QueryBase這個基類 Models/ProductQuery.cs
public class ProductQuery : QueryBase{public string CategoryName { get; set; }public string BrandName { get; set; }public string Age { get; set; }public string LowPrice { get; set; }public string HighPrice { get; set; }}
提供了一個有關排序的枚舉 Models/AscDescEnum.cs
public enum AscDescEnum{asc = 0,desc = 1}
模擬一個數據庫訪問層,提供2個方法,一個方法獲取所有的Product集合,另一個方法根據ProductQuery獲取Product的集合。
展開
在HomeController中:
○ 提供一個action方法返回有關類別的json對象
○ 提供一個action方法返回有關品牌的json對象
○ 提供一個action方法返回有關年限的json對象
○ 提供一個action方法返回有關產品第一頁的json對象
○ 提供一個action方法,根據搜索、排序、分頁條件返回json對象
public class HomeController : Controller{public ActionResult Index(){return View();}//品牌public ActionResult GetBrandsJson(){var allProducts = Database.GetProducts();var result = from p in allProductsgroup p by p.Brandinto gselect new {brand = g.Key};return Json(result, JsonRequestBehavior.AllowGet);}//類別public ActionResult GetCategoriesJson(){var allProducts = Database.GetProducts();var result = from p in allProductsgroup p by p.Categoryinto gselect new {category = g.Key};return Json(result, JsonRequestBehavior.AllowGet);}//年限public ActionResult GetAgesJson(){var allProducts = Database.GetProducts();var result = from p in allProductsgroup p by p.Ageinto gselect new { age = g.Key };return Json(result, JsonRequestBehavior.AllowGet);}//加載產品第一頁private string _categoryName = string.Empty;private string _brandName = string.Empty;private string _age = string.Empty;private string _lowerPrice = string.Empty;private string _higherPrice = string.Empty;public ActionResult GetFirstPage(){var temp = new ProductQuery(){PageIndex = 1,PageSize = 6,Age = _age,BrandName = _brandName,CategoryName = _categoryName,HighPrice = _higherPrice,LowPrice = _lowerPrice,PaiXu = (short)AscDescEnum.asc};int totalNum = 0;var allProducts = Database.GetPageProducts(temp, out totalNum);var result = from p in allProductsselect new {p.Name, p.Brand, p.Category, p.Age, p.Description, p.Price};var tempTotal = Convert.ToInt32(Math.Ceiling((double)(totalNum / 6))) +1;var jsonResult = new { total = tempTotal, rows = result };return Json(jsonResult, JsonRequestBehavior.AllowGet);}//根據搜索排序分頁條件加載[HttpPost]public ActionResult GetProductsBySearchSortPage(ProductQuery productQuery){int totalNum = 0;var allProducts = Database.GetPageProducts(productQuery, out totalNum);var result = from p in allProductsselect new { p.Name, p.Brand, p.Category, p.Age, p.Description, p.Price };var tempTotal = Convert.ToInt32(Math.Ceiling((double)(totalNum / 6))) + 1;var jsonResult = new { total = tempTotal, rows = result };return Json(jsonResult);}}
在Shared/Layout.cshtml中,相關的css.js必須具備:
<head><meta charset="utf-8" /><meta name="viewport" content="width=device-width" /><title>@ViewBag.Title</title>@Styles.Render("~/Content/css")<link href="~/bootstrap/css/bootstrap.min.css" rel="stylesheet" />@RenderSection("styles", required: false)@Scripts.Render("~/bundles/jquery")<script src="~/bootstrap/js/bootstrap.min.js"></script></head><body>@RenderBody()@RenderSection("scripts", required: false)</body
在Home/Index.cshtml中:
○ 用到了有關分頁的一個jQuery插件http://botmonster.com/jquery-bootpag/
○ 頁面首次記載,異步加載產品的前6條記錄作為第一頁
○ 頁面首次加載,異步加載所有分類作為搜索條件
○ 頁面首次加載,異步加載所有品牌作為搜索條件
○ 頁面首次加載,異步加載所有年限作為搜索條件
○ 點擊搜索條件中的品牌事件
○ 點擊搜索條件中的分類事件
○ 點擊搜索條件中的年限事件
○ 點擊搜索條件中的價格事件
○ 點擊"搜索條件"欄中的搜索標簽事件
@{ViewBag.Title = "Index";Layout = "~/Views/Shared/_Layout.cshtml";}@section styles{<link href="~/Content/ProductList.css" rel="stylesheet" />}<div class="container"><!--搜索條件開始--><div class="row" id="searches"><div class="span12"><table class="table table-condensed table-hover" id="serachtable"><tbody><tr><td class="fristcell">搜索條件</td><td class="secondcontent" id="sccondition"><ul class="tagul"></ul><input type="hidden" value="" name="brand"/><input type="hidden" value="" name="category"/><input type="hidden" value="" name="lowprice"/><input type="hidden" value="" name="highprice"/><input type="hidden" value="" name="age"/><input type="hidden" value="0" name="pricesort"/></td></tr><tr><td class="fristcell">品牌</td><td class="secondcontent" id="pp"></td></tr><tr><td class="fristcell">分類</td><td class="secondcontent" id="fl"></td></tr><tr><td class="fristcell">價格</td><td class="secondcontent" id="jg"><a class="innera" href="javascript:void(0)" lowprice="80" highprice="">80元以下</a><a class="innera" href="javascript:void(0)" lowprice="80" highprice="90">80-90元</a><a class="innera" href="javascript:void(0)" lowprice="90" highprice="100">90-100元</a><a class="innera" href="javascript:void(0)" lowprice="100" highprice="110">100-110元</a><a class="innera" href="javascript:void(0)" lowprice="110" highprice="120">110-120元</a><a class="innera" href="javascript:void(0)" lowprice="120" highprice="130">120-130元</a><a class="innera" href="javascript:void(0)" lowprice="130" highprice="140">130-140元</a><a class="innera" href="javascript:void(0)" lowprice="140" highprice="150">140-150元</a><a class="innera" href="javascript:void(0)" lowprice="150" highprice="160">150-160元</a><a class="innera" href="javascript:void(0)" lowprice="160" highprice="170">160-170元</a><a class="innera" href="javascript:void(0)" lowprice="170" highprice="180">170-180元</a><a class="innera" href="javascript:void(0)" lowprice="180" highprice="190">180-190元</a><a class="innera" href="javascript:void(0)" lowprice="190" highprice="200">190-200元</a><a class="innera" href="javascript:void(0)" lowprice="200" highprice="210">200-210元</a><a class="innera" href="javascript:void(0)" lowprice="210" highprice="220">210-220元</a><a class="innera" href="javascript:void(0)" lowprice="220" highprice="230">220-230元</a><a class="innera" href="javascript:void(0)" lowprice="" highprice="230">230元以上</a></td></tr><tr><td class="fristcell">年限</td><td class="secondcontent" id="nx"></td></tr><tr><td class="fristcell">排序</td><td class="secondcontent" id="px"><a class="innera" href="javascript:void(0)" id="pricesort">價格<span style="margin: 0px;">∧</span><span style="margin: 0px;display: none">∨</span></a></td></tr><tr><td></td><td></td></tr></tbody></table></div></div><!--搜索條件結束--><!--產品開始--><div class="container"><div class="well well-sm"><strong>顯示方式</strong><div class="btn-group"><a href="#" id="list" class="btn btn-default btn-sm"><span class="glyphicon glyphicon-th-list"></span>列表</a> <a href="#" id="grid" class="btn btn-default btn-sm"><spanclass="glyphicon glyphicon-th"></span>格子</a></div></div><div id="products" class="row list-group"></div></div><!--產品結束--><!--分頁開始--><div class="row" id="page-selection" style="text-align: center;margin-bottom: 50px;"></div><!--分頁結束--></div>@section scripts{<script src="~/Scripts/jquery.tmpl.min.js"></script><script src="~/Scripts/jquery.bootpag.min.js"></script><script type="text/javascript">$(function () {//加載首頁產品$.getJSON('@Url.Action("GetFirstPage","Home")', function(data) {if (data) {$('#productTemplate').tmpl(data).appendTo('#products');//關於分頁$('#page-selection').bootpag({total: data.total, //初始顯示的頁數maxVisible: 10}).on("page", function (event, num) { //點擊分頁按鈕var productQueryObject = {categoryName: $('#sccondition').find("input[name='category']").val(),brandName: $('#sccondition').find("input[name='brand']").val(),age: $('#sccondition').find("input[name='age']").val(),lowPrice: $('#sccondition').find("input[name='lowprice']").val(),highPrice: $('#sccondition').find("input[name='highprice']").val(),pageIndex: num,pageSize: 6,paiXu: $('#sccondition').find("input[name='pricesort']").val()};$.ajax({type: "POST",url: '@Url.Action("GetProductsBySearchSortPage","Home")',dataType: "json",contentType: "application/json; charset=utf-8",data: JSON.stringify(productQueryObject),success: function (result) {$('#products').empty();$('#productTemplate').tmpl(result).appendTo('#products');//maxVisible 最多可見的頁數$(this).bootpag({ total: result.total});},error: function (error) {alert("有錯誤: " + error.responseText);}});});}});//加載所有品牌$.getJSON('@Url.Action("GetBrandsJson", "Home")', function (data) {$('#pinpaiTemplate').tmpl(data).appendTo('#pp');});//點擊某一品牌$('#pp').on("click", ".innera", function () {//先清空其它已經存在與搜索區域的品牌$('ul.tagul li').find('.pinpaitag').parent().hide();//清空搜索區域中有關品牌的隱藏域$('#sccondition').find("input[name='brand']").val('');//當前a以外的為不選中狀態$('#pp').find('.innera').removeClass('selected');//當前a為選中狀態$(this).addClass('selected');//填充模版並追加到搜索區域$('#pinpaitagTemplate').tmpl({ pinpai: $(this).text() }).appendTo('ul.tagul');//為搜索區域中有關品牌的隱藏域賦值$('#sccondition').find("input[name='brand']").val($(this).text());getProductsBySortOrSearch();});//加載所有類別$.getJSON('@Url.Action("GetCategoriesJson", "Home")', function(data) {$('#leibieTemplate').tmpl(data).appendTo('#fl');});//點擊某一類別$('#fl').on("click", ".innera", function () {//先清空其它已經存在與搜索區域的類別$('ul.tagul li').find('.fenleitag').parent().hide();//清空搜索區域中有關類別的隱藏域$('#sccondition').find("input[name='category']").val('');//當前a以外的為不選中狀態$('#fl').find('.innera').removeClass('selected');//當前a為選中狀態$(this).addClass('selected');//填充模版並追加到搜索區域$('#fenleitagTemplate').tmpl({ fenlei: $(this).text() }).appendTo('ul.tagul');//為搜索區域中有關類別的隱藏域賦值$('#sccondition').find("input[name='category']").val($(this).text());getProductsBySortOrSearch();});//加載所有Age$.getJSON('@Url.Action("GetAgesJson", "Home")', function(data) {$('#ageTemplate').tmpl(data).appendTo('#nx');});//點擊某一年限$('#nx').on("click", ".innera", function () {//先清空其它已經存在與搜索區域的年限$('ul.tagul li').find('.agetag').parent().hide();//清空搜索區域中有關年限的隱藏域$('#sccondition').find("input[name='age']").val('');//當前a以外的為不選中狀態$('#nx').find('.innera').removeClass('selected');//當前a為選中狀態$(this).addClass('selected');//填充模版並追加到搜索區域$('#agetagTemplate').tmpl({ age: $(this).text() }).appendTo('ul.tagul');//為搜索區域中有關年限的隱藏域賦值$('#sccondition').find("input[name='age']").val($(this).text());getProductsBySortOrSearch();});//點擊某一價格$('#jg').on("click", ".innera", function () {//先清空其它已經存在與搜索區域的年限$('ul.tagul li').find('.pricetag').parent().hide();//清空搜索區域中有關價格的隱藏域$('#sccondition').find("input[name='lowprice']").val('');$('#sccondition').find("input[name='highprice']").val('');//當前a以外的為不選中狀態$('#jg').find('.innera').removeClass('selected');//當前a為選中狀態$(this).addClass('selected');//填充模版並追加到搜索區域$('#pricetagTemplate').tmpl({ price: $(this).text() }).appendTo('ul.tagul');//為搜索區域中有關價格的隱藏域賦值$('#sccondition').find("input[name='lowprice']").val($(this).attr('lowprice'));$('#sccondition').find("input[name='highprice']").val($(this).attr('highprice'));getProductsBySortOrSearch();});//關於產品列表$('#list').click(function(event) {event.preventDefault();$('#products .item').addClass('list-group-item');});//關於產品方格展示$('#grid').click(function(event) {event.preventDefault();$('#products .item').removeClass('list-group-item');$('#products .item').addClass('grid-group-item');});//點擊搜索標簽刪除$('ul.tagul').on("click", "li span", function () {//獲取當前span的class值var temp = $(this).attr('class');if (temp == "tagcontent pinpaitag") {//把品牌中的所有a都設為不選中狀態$('#pp').find('.innera').removeClass('selected');//清空搜索區域中有關品牌的隱藏域$('#sccondition').find("input[name='brand']").val('');} else if (temp == "tagcontent fenleitag") {//把分類中的所有a都設為不選中狀態$('#fl').find('.innera').removeClass('selected');//清空搜索區域中有關分類的隱藏域$('#sccondition').find("input[name='category']").val('');} else if (temp == "tagcontent agetag") {//把年限中的所有a都設為不選中狀態$('#nx').find('.innera').removeClass('selected');//清空搜索區域中有關年限的隱藏域$('#sccondition').find("input[name='age']").val('');} else if (temp == "tagcontent pricetag") {//把價格中的所有a都設為不選中狀態$('#jg').find('.innera').removeClass('selected');//清空搜索區域中有關價格的隱藏域$('#sccondition').find("input[name='lowprice']").val('');$('#sccondition').find("input[name='highprice']").val('');}$(this).parent().hide();getProductsBySortOrSearch();});//鼠標移上搜索標簽$('ul.tagul').on("mouseover", "li span", function() {$(this).css('cursor', 'pointer');$(this).css("background-color", "orangered");});//鼠標移去搜索標簽$('ul.tagul').on("mouseout", "li span", function() {$(this).css('cursor', 'default');$(this).css("background-color", "#5BC0DE");});//點擊排序中的價格排序$('#pricesort').on("click", function() {$(this).find("span").toggle();var temp = $('#sccondition').find("input[name='pricesort']");temp.val(temp.val() == '0' ? '1' : '0');getProductsBySortOrSearch();});});//點擊搜索條件或者升序降序,當前頁為1function getProductsBySortOrSearch() {var productQueryObject = {categoryName: $('#sccondition').find("input[name='category']").val(),brandName: $('#sccondition').find("input[name='brand']").val(),age: $('#sccondition').find("input[name='age']").val(),lowPrice: $('#sccondition').find("input[name='lowprice']").val(),highPrice: $('#sccondition').find("input[name='highprice']").val(),pageIndex: 1,pageSize: 6,paiXu: $('#sccondition').find("input[name='pricesort']").val()};$.ajax({type: "POST",url: '@Url.Action("GetProductsBySearchSortPage","Home")',dataType: "json",contentType: "application/json; charset=utf-8",data: JSON.stringify(productQueryObject),success: function (data) {$('#products').empty();$('#productTemplate').tmpl(data).appendTo('#products');//關於分頁$('#page-selection').bootpag({total: data.total, //初始顯示的頁數maxVisible: 10}).on("page", function (event, num) { //點擊分頁按鈕var productQueryObject = {categoryName: $('#sccondition').find("input[name='category']").val(),brandName: $('#sccondition').find("input[name='brand']").val(),age: $('#sccondition').find("input[name='age']").val(),lowPrice: $('#sccondition').find("input[name='lowprice']").val(),highPrice: $('#sccondition').find("input[name='highprice']").val(),pageIndex: num,pageSize: 6,paiXu: $('#sccondition').find("input[name='pricesort']").val()};$.ajax({type: "POST",url: '@Url.Action("GetProductsBySearchSortPage","Home")',dataType: "json",contentType: "application/json; charset=utf-8",data: JSON.stringify(productQueryObject),success: function (result) {$('#products').empty();$('#productTemplate').tmpl(result).appendTo('#products');//maxVisible 最多可見的頁數$(this).bootpag({ total: result.total });},error: function (error) {alert("有錯誤: " + error.responseText);}});});},error: function (error) {alert("有錯誤: " + error.responseText);}});}</script><!--品牌搜索模版--><script id="pinpaiTemplate" type="text/x-jQuery-tmpl"><a class="innera" href="javascript:void(0)">${brand}</a></script><!--類別搜索模版--><script id="leibieTemplate" type="text/x-jQuery-tmpl"><a class="innera" href="javascript:void(0)">${category}</a></script><!--年限搜索模版--><script id="ageTemplate" type="text/x-jQuery-tmpl"><a class="innera" href="javascript:void(0)">${age}</a></script><!--品牌標簽模版--><script id="pinpaitagTemplate" type="text/x-jQuery-tmpl"><li><span class="tagcontent pinpaitag">品牌:${pinpai} ×</span></li></script><!--分類標簽模版--><script id="fenleitagTemplate" type="text/x-jQuery-tmpl"><li><span class="tagcontent fenleitag">分類:${fenlei} ×</span></li></script><!--價格標簽模版--><script id="pricetagTemplate" type="text/x-jQuery-tmpl"><li><span class="tagcontent pricetag">價格:${price} ×</span></li></script><!--年限標簽模版--><script id="agetagTemplate" type="text/x-jQuery-tmpl"><li><span class="tagcontent agetag">年限:${age} ×</span></li></script><!--產品列表模版--><script id="productTemplate" type="text/x-jQuery-tmpl">{{if rows}}{{each rows}}<div class="item col-xs-4 col-lg-4"><div class="thumbnail"><img class="group list-group-image" src="http://placehold.it/400x250/000/fff" alt="" /><div class="caption"><h4 class="group inner list-group-item-heading">${$value.Name}</h4><p class="group inner list-group-item-text">品牌:${$value.Brand}</p><p class="group inner list-group-item-text">分類:${$value.Category}</p><p class="group inner list-group-item-text">年限:${$value.Age}</p><p class="group inner list-group-item-text">${$value.Description}</p><div class="row"><div class="col-xs-12 col-md-6"><p class="lead">¥ ${$value.Price}</p></div><div class="col-xs-12 col-md-6"><a class="btn btn-success " href="javascript:void(0)">購買</a></div></div></div></div></div>{{/each}}{{else}}<span>沒有記錄</span>{{/if}}</script>}

